diff --git a/massmeta/code/modules/antags/wizard/equipment/spellbook_entries/offensive.dm b/massmeta/code/modules/antags/wizard/equipment/spellbook_entries/offensive.dm
index 979434aa47539..fe97eaf69bc14 100644
--- a/massmeta/code/modules/antags/wizard/equipment/spellbook_entries/offensive.dm
+++ b/massmeta/code/modules/antags/wizard/equipment/spellbook_entries/offensive.dm
@@ -1,7 +1,7 @@
#define SPELLBOOK_CATEGORY_OFFENSIVE "Offensive"
// Offensive wizard spells
-datum/spellbook_entry/testicular_torsion
+/datum/spellbook_entry/testicular_torsion
name = "Testicular Torsion"
desc = "A dark spell capable of exploding victim's balls."
spell_type = /datum/action/cooldown/spell/touch/testicular_torsion
diff --git a/massmeta/features/nanites/code/Z_edits/hud_edits.dm b/massmeta/features/nanites/code/Z_edits/hud_edits.dm
new file mode 100644
index 0000000000000..d58c5d4cf5368
--- /dev/null
+++ b/massmeta/features/nanites/code/Z_edits/hud_edits.dm
@@ -0,0 +1,20 @@
+/mob/New()
+ // add our nanite huds to everyone so they can (if possible) actually see nanites.
+ hud_possible += list(NANITE_HUD, DIAG_NANITE_FULL_HUD)
+ return ..()
+
+/datum/atom_hud/data/human/medical/New()
+ . = ..()
+ hud_icons += list(NANITE_HUD)
+
+/datum/atom_hud/data/human/security/advanced/New()
+ . = ..()
+ hud_icons += list(NANITE_HUD)
+
+/datum/atom_hud/data/diagnostic/basic/New()
+ . = ..()
+ hud_icons += list(DIAG_NANITE_FULL_HUD)
+
+/datum/atom_hud/data/diagnostic/advanced/New()
+ . = ..()
+ hud_icons += list(DIAG_NANITE_FULL_HUD)
diff --git a/massmeta/features/nanites/code/Z_edits/research_edits.dm b/massmeta/features/nanites/code/Z_edits/research_edits.dm
new file mode 100644
index 0000000000000..bc7996132bd77
--- /dev/null
+++ b/massmeta/features/nanites/code/Z_edits/research_edits.dm
@@ -0,0 +1,3 @@
+/datum/controller/subsystem/research/Initialize()
+ . = ..()
+ point_types += list(TECHWEB_POINT_TYPE_NANITES = "Nanite Res.")
diff --git a/massmeta/features/nanites/code/areas.dm b/massmeta/features/nanites/code/areas.dm
new file mode 100644
index 0000000000000..f8d6a4db06fdd
--- /dev/null
+++ b/massmeta/features/nanites/code/areas.dm
@@ -0,0 +1,4 @@
+/area/station/science/nanite
+ name = "Nanite Lab"
+ icon = 'massmeta/features/nanites/icons/areas.dmi'
+ icon_state = "nanite_lab"
diff --git a/massmeta/features/nanites/code/circuitboards.dm b/massmeta/features/nanites/code/circuitboards.dm
new file mode 100644
index 0000000000000..d404e9743e813
--- /dev/null
+++ b/massmeta/features/nanites/code/circuitboards.dm
@@ -0,0 +1,38 @@
+/obj/item/circuitboard/computer/nanite_chamber_control
+ name = "Nanite Chamber Control"
+ greyscale_colors = CIRCUIT_COLOR_SCIENCE
+ build_path = /obj/machinery/computer/nanite_chamber_control
+
+/obj/item/circuitboard/computer/nanite_cloud_controller
+ name = "Nanite Cloud Control"
+ greyscale_colors = CIRCUIT_COLOR_SCIENCE
+ build_path = /obj/machinery/computer/nanite_cloud_controller
+
+/obj/item/circuitboard/machine/nanite_chamber
+ name = "Nanite Chamber"
+ greyscale_colors = CIRCUIT_COLOR_SCIENCE
+ build_path = /obj/machinery/nanite_chamber
+ req_components = list(
+ /obj/item/stock_parts/micro_laser = 2,
+ /obj/item/stock_parts/scanning_module = 1,
+ /obj/item/stock_parts/servo = 1,
+ )
+
+/obj/item/circuitboard/machine/nanite_program_hub
+ name = "Nanite Program Hub"
+ greyscale_colors = CIRCUIT_COLOR_SCIENCE
+ build_path = /obj/machinery/nanite_program_hub
+ req_components = list(
+ /obj/item/stock_parts/matter_bin = 1,
+ /obj/item/stock_parts/servo = 1,
+ )
+
+/obj/item/circuitboard/machine/nanite_programmer
+ name = "Nanite Programmer"
+ greyscale_colors = CIRCUIT_COLOR_SCIENCE
+ build_path = /obj/machinery/nanite_programmer
+ req_components = list(
+ /obj/item/stock_parts/servo = 2,
+ /obj/item/stock_parts/micro_laser = 2,
+ /obj/item/stock_parts/scanning_module = 1,
+ )
diff --git a/massmeta/features/nanites/code/designs/_nanite.dm b/massmeta/features/nanites/code/designs/_nanite.dm
new file mode 100644
index 0000000000000..b878ffc9ffb5b
--- /dev/null
+++ b/massmeta/features/nanites/code/designs/_nanite.dm
@@ -0,0 +1,11 @@
+/datum/design/nanites
+ name = "None"
+ desc = "Warn a coder if you see this."
+ id = "default_nanites"
+ build_type = NONE
+ construction_time = 50
+ category = list()
+ research_icon = 'massmeta/features/nanites/icons/nanite_device.dmi'
+ research_icon_state = "nanite_program"
+ ///The nanite program researching this will unlock.
+ var/program_type = /datum/nanite_program
diff --git a/massmeta/features/nanites/code/designs/_tools.dm b/massmeta/features/nanites/code/designs/_tools.dm
new file mode 100644
index 0000000000000..42765533b8ee5
--- /dev/null
+++ b/massmeta/features/nanites/code/designs/_tools.dm
@@ -0,0 +1,63 @@
+/datum/design/nanite_remote
+ name = "Nanite Remote"
+ desc = "Allows for the construction of a nanite remote."
+ id = "nanite_remote"
+ build_type = PROTOLATHE
+ materials = list(/datum/material/glass = 500, /datum/material/iron = 500)
+ build_path = /obj/item/nanite_remote
+ category = list(RND_CATEGORY_CONSTRUCTION + RND_SUBCATEGORY_CONSTRUCTION_ELECTRONICS)
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
+
+/datum/design/nanite_comm_remote
+ name = "Nanite Communication Remote"
+ desc = "Allows for the construction of a nanite communication remote."
+ id = "nanite_comm_remote"
+ build_type = PROTOLATHE
+ materials = list(/datum/material/glass = 500, /datum/material/iron = 500)
+ build_path = /obj/item/nanite_remote/comm
+ category = list(RND_CATEGORY_CONSTRUCTION + RND_SUBCATEGORY_CONSTRUCTION_ELECTRONICS)
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
+
+/datum/design/nanite_scanner
+ name = "Nanite Scanner"
+ desc = "Allows for the construction of a nanite scanner."
+ id = "nanite_scanner"
+ build_type = PROTOLATHE
+ materials = list(/datum/material/glass = 500, /datum/material/iron = 500)
+ build_path = /obj/item/nanite_scanner
+ category = list(RND_CATEGORY_CONSTRUCTION + RND_SUBCATEGORY_CONSTRUCTION_ELECTRONICS)
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
+
+/datum/design/nanite_disk
+ name = "Nanite Program Disk"
+ desc = "Stores nanite programs."
+ id = "nanite_disk"
+ build_type = PROTOLATHE
+ materials = list(/datum/material/iron = 300, /datum/material/glass = 100)
+ build_path = /obj/item/disk/nanite_program
+ category = list(RND_CATEGORY_CONSTRUCTION + RND_SUBCATEGORY_CONSTRUCTION_ELECTRONICS)
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
+
+/datum/design/board/nanite_chamber
+ name = "Machine Design (Nanite Chamber Board)"
+ desc = "The circuit board for a Nanite Chamber."
+ id = "nanite_chamber"
+ build_path = /obj/item/circuitboard/machine/nanite_chamber
+ category = list(RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_RESEARCH)
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
+
+/datum/design/board/nanite_programmer
+ name = "Machine Design (Nanite Programmer Board)"
+ desc = "The circuit board for a Nanite Programmer."
+ id = "nanite_programmer"
+ build_path = /obj/item/circuitboard/machine/nanite_programmer
+ category = list(RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_RESEARCH)
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
+
+/datum/design/board/nanite_program_hub
+ name = "Machine Design (Nanite Program Hub Board)"
+ desc = "The circuit board for a Nanite Program Hub."
+ id = "nanite_program_hub"
+ build_path = /obj/item/circuitboard/machine/nanite_program_hub
+ category = list(RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_RESEARCH)
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
diff --git a/massmeta/features/nanites/code/designs/nanite_augmentation.dm b/massmeta/features/nanites/code/designs/nanite_augmentation.dm
new file mode 100644
index 0000000000000..4642695a95e22
--- /dev/null
+++ b/massmeta/features/nanites/code/designs/nanite_augmentation.dm
@@ -0,0 +1,48 @@
+/datum/design/nanites/nervous
+ name = "Nerve Support"
+ desc = "The nanites act as a secondary nervous system, reducing the amount of time the host is stunned."
+ id = "nervous_nanites"
+ category = list(NANITES_CATEGORY_AUGMENTATION)
+ program_type = /datum/nanite_program/nervous
+
+/datum/design/nanites/hardening
+ name = "Dermal Hardening"
+ desc = "The nanites form a mesh under the host's skin, protecting them from melee and bullet impacts."
+ id = "hardening_nanites"
+ category = list(NANITES_CATEGORY_AUGMENTATION)
+ program_type = /datum/nanite_program/hardening
+
+/datum/design/nanites/refractive
+ name = "Dermal Refractive Surface"
+ desc = "The nanites form a membrane above the host's skin, reducing the effect of laser and energy impacts."
+ id = "refractive_nanites"
+ category = list(NANITES_CATEGORY_AUGMENTATION)
+ program_type = /datum/nanite_program/refractive
+
+/datum/design/nanites/coagulating
+ name = "Rapid Coagulation"
+ desc = "The nanites induce rapid coagulation when the host is wounded, dramatically reducing bleeding rate."
+ id = "coagulating_nanites"
+ category = list(NANITES_CATEGORY_AUGMENTATION)
+ program_type = /datum/nanite_program/coagulating
+
+/datum/design/nanites/conductive
+ name = "Electric Conduction"
+ desc = "The nanites act as a grounding rod for electric shocks, protecting the host. Shocks can still damage the nanites themselves."
+ id = "conductive_nanites"
+ category = list(NANITES_CATEGORY_AUGMENTATION)
+ program_type = /datum/nanite_program/conductive
+
+/datum/design/nanites/adrenaline
+ name = "Adrenaline Burst"
+ desc = "The nanites cause a burst of adrenaline when triggered, waking the host from stuns and temporarily increasing their speed."
+ id = "adrenaline_nanites"
+ category = list(NANITES_CATEGORY_AUGMENTATION)
+ program_type = /datum/nanite_program/adrenaline
+
+/datum/design/nanites/mindshield
+ name = "Mental Barrier"
+ desc = "The nanites form a protective membrane around the host's brain, shielding them from abnormal influences while they're active."
+ id = "mindshield_nanites"
+ category = list(NANITES_CATEGORY_AUGMENTATION)
+ program_type = /datum/nanite_program/mindshield
diff --git a/massmeta/features/nanites/code/designs/nanite_defective.dm b/massmeta/features/nanites/code/designs/nanite_defective.dm
new file mode 100644
index 0000000000000..04571380bd1fa
--- /dev/null
+++ b/massmeta/features/nanites/code/designs/nanite_defective.dm
@@ -0,0 +1,55 @@
+/datum/design/nanites/glitch
+ name = "Glitch"
+ desc = "A heavy software corruption that causes nanites to gradually break down."
+ id = "glitch_nanites"
+ category = list(NANITES_CATEGORY_DEFECTIVE)
+ program_type = /datum/nanite_program/glitch
+
+/datum/design/nanites/necrotic
+ name = "Necrosis"
+ desc = "The nanites attack internal tissues indiscriminately, causing widespread damage."
+ id = "necrotic_nanites"
+ category = list(NANITES_CATEGORY_DEFECTIVE)
+ program_type = /datum/nanite_program/necrotic
+
+/datum/design/nanites/toxic
+ name = "Toxin Buildup"
+ desc = "The nanites cause a slow but constant toxin buildup inside the host."
+ id = "toxic_nanites"
+ category = list(NANITES_CATEGORY_DEFECTIVE)
+ program_type = /datum/nanite_program/toxic
+
+/datum/design/nanites/suffocating
+ name = "Hypoxemia"
+ desc = "The nanites prevent the host's blood from absorbing oxygen efficiently."
+ id = "suffocating_nanites"
+ category = list(NANITES_CATEGORY_DEFECTIVE)
+ program_type = /datum/nanite_program/suffocating
+
+/datum/design/nanites/brain_misfire
+ name = "Brain Misfire"
+ desc = "The nanites interfere with neural pathways, causing minor psychological disturbances."
+ id = "brainmisfire_nanites"
+ category = list(NANITES_CATEGORY_DEFECTIVE)
+ program_type = /datum/nanite_program/brain_misfire
+
+/datum/design/nanites/skin_decay
+ name = "Dermalysis"
+ desc = "The nanites attack skin cells, causing irritation, rashes, and minor damage."
+ id = "skindecay_nanites"
+ category = list(NANITES_CATEGORY_DEFECTIVE)
+ program_type = /datum/nanite_program/skin_decay
+
+/datum/design/nanites/nerve_decay
+ name = "Nerve Decay"
+ desc = "The nanites attack the host's nerves, causing lack of coordination and short bursts of paralysis."
+ id = "nervedecay_nanites"
+ category = list(NANITES_CATEGORY_DEFECTIVE)
+ program_type = /datum/nanite_program/nerve_decay
+
+/datum/design/nanites/brain_decay
+ name = "Brain-Eating Nanites"
+ desc = "Damages brain cells, gradually decreasing the host's cognitive functions."
+ id = "braindecay_nanites"
+ category = list(NANITES_CATEGORY_DEFECTIVE)
+ program_type = /datum/nanite_program/brain_decay
diff --git a/massmeta/features/nanites/code/designs/nanite_medical.dm b/massmeta/features/nanites/code/designs/nanite_medical.dm
new file mode 100644
index 0000000000000..ff55c2527b374
--- /dev/null
+++ b/massmeta/features/nanites/code/designs/nanite_medical.dm
@@ -0,0 +1,71 @@
+/datum/design/nanites/regenerative
+ name = "Accelerated Regeneration"
+ desc = "The nanites boost the host's natural regeneration, increasing their healing speed."
+ id = "regenerative_nanites"
+ category = list(NANITE_CATEGORY_MEDICAL)
+ program_type = /datum/nanite_program/regenerative
+
+/datum/design/nanites/regenerative_advanced
+ name = "Bio-Reconstruction"
+ desc = "The nanites manually repair and replace organic cells, acting much faster than normal regeneration. \
+ However, this program cannot detect the difference between harmed and unharmed, causing it to consume nanites even if it has no effect."
+ id = "regenerative_plus_nanites"
+ category = list(NANITE_CATEGORY_MEDICAL)
+ program_type = /datum/nanite_program/regenerative_advanced
+
+/datum/design/nanites/temperature
+ name = "Temperature Adjustment"
+ desc = "The nanites adjust the host's internal temperature to an ideal level."
+ id = "temperature_nanites"
+ category = list(NANITE_CATEGORY_MEDICAL)
+ program_type = /datum/nanite_program/temperature
+
+/datum/design/nanites/purging
+ name = "Blood Purification"
+ desc = "The nanites purge toxins and chemicals from the host's bloodstream."
+ id = "purging_nanites"
+ category = list(NANITE_CATEGORY_MEDICAL)
+ program_type = /datum/nanite_program/purging
+
+/datum/design/nanites/purging_advanced
+ name = "Selective Blood Purification"
+ desc = "The nanites purge toxins and dangerous chemicals from the host's bloodstream, while ignoring beneficial chemicals. \
+ The added processing power required to analyze the chemicals severely increases the nanite consumption rate."
+ id = "purging_plus_nanites"
+ category = list(NANITE_CATEGORY_MEDICAL)
+ program_type = /datum/nanite_program/purging_advanced
+
+/datum/design/nanites/brain_heal
+ name = "Neural Regeneration"
+ desc = "The nanites fix neural connections in the host's brain, reversing brain damage and minor traumas."
+ id = "brainheal_nanites"
+ category = list(NANITE_CATEGORY_MEDICAL)
+ program_type = /datum/nanite_program/brain_heal
+
+/datum/design/nanites/brain_heal_advanced
+ name = "Neural Reimaging"
+ desc = "The nanites are able to backup and restore the host's neural connections, potentially replacing entire chunks of missing or damaged brain matter."
+ id = "brainheal_plus_nanites"
+ category = list(NANITE_CATEGORY_MEDICAL)
+ program_type = /datum/nanite_program/brain_heal_advanced
+
+/datum/design/nanites/blood_restoring
+ name = "Blood Regeneration"
+ desc = "The nanites stimulate and boost blood cell production in the host."
+ id = "bloodheal_nanites"
+ category = list(NANITE_CATEGORY_MEDICAL)
+ program_type = /datum/nanite_program/blood_restoring
+
+/datum/design/nanites/repairing
+ name = "Mechanical Repair"
+ desc = "The nanites fix damage in the host's mechanical limbs."
+ id = "repairing_nanites"
+ category = list(NANITE_CATEGORY_MEDICAL)
+ program_type = /datum/nanite_program/repairing
+
+/datum/design/nanites/defib
+ name = "Defibrillation"
+ desc = "The nanites, when triggered, send a defibrillating shock to the host's heart."
+ id = "defib_nanites"
+ category = list(NANITE_CATEGORY_MEDICAL)
+ program_type = /datum/nanite_program/defib
diff --git a/massmeta/features/nanites/code/designs/nanite_protocols.dm b/massmeta/features/nanites/code/designs/nanite_protocols.dm
new file mode 100644
index 0000000000000..b5fe64f902611
--- /dev/null
+++ b/massmeta/features/nanites/code/designs/nanite_protocols.dm
@@ -0,0 +1,56 @@
+/datum/design/nanites/kickstart
+ name = "Kickstart Protocol"
+ desc = "Replication Protocol: the nanites focus on early growth, heavily boosting replication rate for a few minutes after the initial implantation."
+ id = "kickstart_nanites"
+ category = list(NANITES_CATEGORY_PROTOCOLS)
+ program_type = /datum/nanite_program/protocol/kickstart
+
+/datum/design/nanites/factory
+ name = "Factory Protocol"
+ desc = "Replication Protocol: the nanites build a factory matrix within the host, gradually increasing replication speed over time. The factory decays if the protocol is not active."
+ id = "factory_nanites"
+ category = list(NANITES_CATEGORY_PROTOCOLS)
+ program_type = /datum/nanite_program/protocol/factory
+
+/datum/design/nanites/tinker
+ name = "Tinker Protocol"
+ desc = "Replication Protocol: the nanites learn to use metallic material in the host's bloodstream to speed up the replication process."
+ id = "tinker_nanites"
+ category = list(NANITES_CATEGORY_PROTOCOLS)
+ program_type = /datum/nanite_program/protocol/tinker
+
+/datum/design/nanites/offline
+ name = "Offline Production Protocol"
+ desc = "Replication Protocol: while the host is asleep or otherwise unconcious, the nanites exploit the reduced interference to replicate more quickly."
+ id = "offline_nanites"
+ category = list(NANITES_CATEGORY_PROTOCOLS)
+ program_type = /datum/nanite_program/protocol/offline
+
+/datum/design/nanites/hive
+ name = "Hive Protocol"
+ desc = "Storage Protocol: the nanites use a more efficient grid arrangment for volume storage, increasing maximum volume in a host."
+ id = "hive_nanites"
+ category = list(NANITES_CATEGORY_PROTOCOLS)
+ program_type = /datum/nanite_program/protocol/hive
+
+/datum/design/nanites/zip
+ name = "Zip Protocol"
+ desc = "Storage Protocol: the nanites are disassembled and compacted when unused, greatly increasing the maximum volume while in a host. However, the process slows down the replication rate slightly."
+ id = "zip_nanites"
+ category = list(NANITES_CATEGORY_PROTOCOLS)
+ program_type = /datum/nanite_program/protocol/zip
+
+/datum/design/nanites/free_range
+ name = "Free-range Protocol"
+ desc = "Storage Protocol: the nanites discard their default storage protocols in favour of a cheaper and more organic approach. Reduces maximum volume, but increases the replication rate."
+ id = "free_range_nanites"
+ category = list(NANITES_CATEGORY_PROTOCOLS)
+ program_type = /datum/nanite_program/protocol/free_range
+
+/datum/design/nanites/unsafe_storage
+ name = "S.L.O. Protocol"
+ desc = "Storage Protocol: 'S.L.O.P.', or Storage Level Override Protocol, completely disables the safety measures normally present in nanites,\
+ allowing them to reach much higher saturation levels, but at the risk of causing internal damage to the host."
+ id = "unsafe_storage_nanites"
+ category = list(NANITES_CATEGORY_PROTOCOLS)
+ program_type = /datum/nanite_program/protocol/unsafe_storage
diff --git a/massmeta/features/nanites/code/designs/nanite_sensor.dm b/massmeta/features/nanites/code/designs/nanite_sensor.dm
new file mode 100644
index 0000000000000..02daadb61fc1e
--- /dev/null
+++ b/massmeta/features/nanites/code/designs/nanite_sensor.dm
@@ -0,0 +1,48 @@
+/datum/design/nanites/sensor_health
+ name = "Health Sensor"
+ desc = "The nanites receive a signal when the host's health is above/below a certain percentage."
+ id = "sensor_health_nanites"
+ category = list(NANITES_CATEGORY_SENSOR)
+ program_type = /datum/nanite_program/sensor/health
+
+/datum/design/nanites/sensor_damage
+ name = "Damage Sensor"
+ desc = "The nanites receive a signal when a host's specific damage type is above/below a target value."
+ id = "sensor_damage_nanites"
+ category = list(NANITES_CATEGORY_SENSOR)
+ program_type = /datum/nanite_program/sensor/damage
+
+/datum/design/nanites/sensor_crit
+ name = "Critical Health Sensor"
+ desc = "The nanites receive a signal when the host first reaches critical health."
+ id = "sensor_crit_nanites"
+ category = list(NANITES_CATEGORY_SENSOR)
+ program_type = /datum/nanite_program/sensor/crit
+
+/datum/design/nanites/sensor_death
+ name = "Death Sensor"
+ desc = "The nanites receive a signal when they detect the host is dead."
+ id = "sensor_death_nanites"
+ category = list(NANITES_CATEGORY_SENSOR)
+ program_type = /datum/nanite_program/sensor/death
+
+/datum/design/nanites/sensor_voice
+ name = "Voice Sensor"
+ desc = "Sends a signal when the nanites hear a determined word or sentence."
+ id = "sensor_voice_nanites"
+ category = list(NANITES_CATEGORY_SENSOR)
+ program_type = /datum/nanite_program/sensor/voice
+
+/datum/design/nanites/sensor_nanite_volume
+ name = "Nanite Volume Sensor"
+ desc = "The nanites receive a signal when the nanite supply is above/below a certain percentage."
+ id = "sensor_nanite_volume"
+ category = list(NANITES_CATEGORY_SENSOR)
+ program_type = /datum/nanite_program/sensor/nanite_volume
+
+/datum/design/nanites/sensor_species
+ name = "Species Sensor"
+ desc = "When triggered, the nanites scan the host to determine their species and output a signal depending on the conditions set in the settings."
+ id = "sensor_species_nanites"
+ category = list(NANITES_CATEGORY_SENSOR)
+ program_type = /datum/nanite_program/sensor/species
diff --git a/massmeta/features/nanites/code/designs/nanite_suppression.dm b/massmeta/features/nanites/code/designs/nanite_suppression.dm
new file mode 100644
index 0000000000000..4ef27f62d2a25
--- /dev/null
+++ b/massmeta/features/nanites/code/designs/nanite_suppression.dm
@@ -0,0 +1,83 @@
+/datum/design/nanites/shock
+ name = "Electric Shock"
+ desc = "The nanites shock the host when triggered. Destroys a large amount of nanites!"
+ id = "shock_nanites"
+ category = list(NANITES_CATEGORY_SUPPRESSION)
+ program_type = /datum/nanite_program/shocking
+
+/datum/design/nanites/stun
+ name = "Neural Shock"
+ desc = "The nanites pulse the host's nerves when triggered, inapacitating them for a short period."
+ id = "stun_nanites"
+ category = list(NANITES_CATEGORY_SUPPRESSION)
+ program_type = /datum/nanite_program/stun
+
+/datum/design/nanites/sleepy
+ name = "Sleep Induction"
+ desc = "The nanites cause rapid narcolepsy when triggered."
+ id = "sleep_nanites"
+ category = list(NANITES_CATEGORY_SUPPRESSION)
+ program_type = /datum/nanite_program/sleepy
+
+/datum/design/nanites/paralyzing
+ name = "Paralysis"
+ desc = "The nanites actively suppress nervous pulses, effectively paralyzing the host."
+ id = "paralyzing_nanites"
+ category = list(NANITES_CATEGORY_SUPPRESSION)
+ program_type = /datum/nanite_program/paralyzing
+
+/datum/design/nanites/fake_death
+ name = "Death Simulation"
+ desc = "The nanites induce a death-like coma into the host, able to fool most medical scans."
+ id = "fakedeath_nanites"
+ category = list(NANITES_CATEGORY_SUPPRESSION)
+ program_type = /datum/nanite_program/fake_death
+
+/datum/design/nanites/pacifying
+ name = "Pacification"
+ desc = "The nanites suppress the aggression center of the brain, preventing the host from causing direct harm to others."
+ id = "pacifying_nanites"
+ category = list(NANITES_CATEGORY_SUPPRESSION)
+ program_type = /datum/nanite_program/pacifying
+
+/datum/design/nanites/blinding
+ name = "Blindness"
+ desc = "The nanites suppress the host's ocular nerves, blinding them while they're active."
+ id = "blinding_nanites"
+ category = list(NANITES_CATEGORY_SUPPRESSION)
+ program_type = /datum/nanite_program/blinding
+
+/datum/design/nanites/mute
+ name = "Mute"
+ desc = "The nanites suppress the host's speech, making them mute while they're active."
+ id = "mute_nanites"
+ category = list(NANITES_CATEGORY_SUPPRESSION)
+ program_type = /datum/nanite_program/mute
+
+/datum/design/nanites/voice
+ name = "Skull Echo"
+ desc = "The nanites echo a synthesized message inside the host's skull."
+ id = "voice_nanites"
+ category = list(NANITES_CATEGORY_SUPPRESSION)
+ program_type = /datum/nanite_program/comm/voice
+
+/datum/design/nanites/speech
+ name = "Forced Speech"
+ desc = "The nanites force the host to say a pre-programmed sentence when triggered."
+ id = "speech_nanites"
+ category = list(NANITES_CATEGORY_SUPPRESSION)
+ program_type = /datum/nanite_program/comm/speech
+
+/datum/design/nanites/good_mood
+ name = "Happiness Enhancer"
+ desc = "The nanites synthesize serotonin inside the host's brain, creating an artificial sense of happiness."
+ id = "good_mood_nanites"
+ category = list(NANITES_CATEGORY_SUPPRESSION)
+ program_type = /datum/nanite_program/good_mood
+
+/datum/design/nanites/bad_mood
+ name = "Happiness Suppressor"
+ desc = "The nanites suppress the production of serotonin inside the host's brain, creating an artificial state of depression."
+ id = "bad_mood_nanites"
+ category = list(NANITES_CATEGORY_SUPPRESSION)
+ program_type = /datum/nanite_program/bad_mood
diff --git a/massmeta/features/nanites/code/designs/nanite_utilities.dm b/massmeta/features/nanites/code/designs/nanite_utilities.dm
new file mode 100644
index 0000000000000..431cba93b306c
--- /dev/null
+++ b/massmeta/features/nanites/code/designs/nanite_utilities.dm
@@ -0,0 +1,100 @@
+/datum/design/nanites/metabolic_synthesis
+ name = "Metabolic Synthesis"
+ desc = "The nanites use the metabolic cycle of the host to speed up their replication rate, using their extra nutrition as fuel."
+ id = "metabolic_nanites"
+ category = list(NANITE_CATEGORY_UTILITIES)
+ program_type = /datum/nanite_program/metabolic_synthesis
+
+/datum/design/nanites/viral
+ name = "Viral Replica"
+ desc = "The nanites constantly send encrypted signals attempting to forcefully copy their own programming into other nanite clusters."
+ id = "viral_nanites"
+ category = list(NANITE_CATEGORY_UTILITIES)
+ program_type = /datum/nanite_program/viral
+
+/datum/design/nanites/self_scan
+ name = "Host Scan"
+ desc = "The nanites display a detailed readout of a body scan to the host."
+ id = "selfscan_nanites"
+ category = list(NANITE_CATEGORY_UTILITIES)
+ program_type = /datum/nanite_program/self_scan
+
+/datum/design/nanites/dermal_button
+ name = "Dermal Button"
+ desc = "Displays a button on the host's skin, which can be used to send a signal to the nanites."
+ id = "dermal_button_nanites"
+ category = list(NANITE_CATEGORY_UTILITIES)
+ program_type = /datum/nanite_program/dermal_button
+
+/datum/design/nanites/stealth
+ name = "Stealth"
+ desc = "The nanites hide their activity and programming from superficial scans."
+ id = "stealth_nanites"
+ category = list(NANITE_CATEGORY_UTILITIES)
+ program_type = /datum/nanite_program/stealth
+
+/datum/design/nanites/reduced_diagnostics
+ name = "Reduced Diagnostics"
+ desc = "Disables some high-cost diagnostics in the nanites, making them unable to communicate their program list to portable scanners. \
+ Doing so saves some power, slightly increasing their replication speed."
+ id = "red_diag_nanites"
+ category = list(NANITE_CATEGORY_UTILITIES)
+ program_type = /datum/nanite_program/reduced_diagnostics
+
+/datum/design/nanites/access
+ name = "Subdermal ID"
+ desc = "The nanites store the host's ID access rights in a subdermal magnetic strip. Updates when triggered, copying the host's current access."
+ id = "access_nanites"
+ category = list(NANITE_CATEGORY_UTILITIES)
+ program_type = /datum/nanite_program/access
+
+/datum/design/nanites/relay
+ name = "Relay"
+ desc = "The nanites receive and relay long-range nanite signals."
+ id = "relay_nanites"
+ category = list(NANITE_CATEGORY_UTILITIES)
+ program_type = /datum/nanite_program/relay
+
+/datum/design/nanites/repeater
+ name = "Signal Repeater"
+ desc = "When triggered, sends another signal to the nanites, optionally with a delay."
+ id = "repeater_nanites"
+ category = list(NANITE_CATEGORY_UTILITIES)
+ program_type = /datum/nanite_program/sensor/repeat
+
+/datum/design/nanites/relay_repeater
+ name = "Relay Signal Repeater"
+ desc = "When triggered, sends another signal to a relay channel, optionally with a delay."
+ id = "relay_repeater_nanites"
+ category = list(NANITE_CATEGORY_UTILITIES)
+ program_type = /datum/nanite_program/sensor/relay_repeat
+
+/datum/design/nanites/emp
+ name = "Electromagnetic Resonance"
+ desc = "The nanites cause an electromagnetic pulse around the host when triggered. Will corrupt other nanite programs!"
+ id = "emp_nanites"
+ category = list(NANITE_CATEGORY_UTILITIES)
+ program_type = /datum/nanite_program/emp
+
+/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."
+ id = "spreading_nanites"
+ category = list(NANITE_CATEGORY_UTILITIES)
+ program_type = /datum/nanite_program/spreading
+
+/datum/design/nanites/nanite_sting
+ name = "Nanite Sting"
+ desc = "When triggered, projects a nearly invisible spike of nanites that attempts to infect a nearby non-host with a copy of the host's nanites cluster."
+ id = "nanite_sting_nanites"
+ category = list(NANITE_CATEGORY_UTILITIES)
+ program_type = /datum/nanite_program/nanite_sting
+
+/datum/design/nanites/mitosis
+ name = "Mitosis"
+ desc = "The nanites gain the ability to self-replicate, using bluespace to power the process, instead of drawing from a template. This rapidly speeds up the replication rate,\
+ but it causes occasional software errors due to faulty copies. Not compatible with cloud sync."
+ id = "mitosis_nanites"
+ category = list(NANITE_CATEGORY_UTILITIES)
+ program_type = /datum/nanite_program/mitosis
diff --git a/massmeta/features/nanites/code/designs/nanite_weaponized.dm b/massmeta/features/nanites/code/designs/nanite_weaponized.dm
new file mode 100644
index 0000000000000..ec3858f3df57e
--- /dev/null
+++ b/massmeta/features/nanites/code/designs/nanite_weaponized.dm
@@ -0,0 +1,70 @@
+/datum/design/nanites/flesh_eating
+ name = "Cellular Breakdown"
+ desc = "The nanites destroy cellular structures in the host's body, causing brute damage."
+ id = "flesheating_nanites"
+ category = list(NANITES_CATEGORY_WEAPONIZED)
+ program_type = /datum/nanite_program/flesh_eating
+
+/datum/design/nanites/poison
+ name = "Poisoning"
+ desc = "The nanites deliver poisonous chemicals to the host's internal organs, causing toxin damage and vomiting."
+ id = "poison_nanites"
+ category = list(NANITES_CATEGORY_WEAPONIZED)
+ program_type = /datum/nanite_program/poison
+
+/datum/design/nanites/memory_leak
+ name = "Memory Leak"
+ desc = "This program invades the memory space used by other programs, causing frequent corruptions and errors."
+ id = "memleak_nanites"
+ category = list(NANITES_CATEGORY_WEAPONIZED)
+ program_type = /datum/nanite_program/memory_leak
+
+/datum/design/nanites/aggressive_replication
+ name = "Aggressive Replication"
+ desc = "Nanites will consume organic matter to improve their replication rate, damaging the host."
+ id = "aggressive_nanites"
+ category = list(NANITES_CATEGORY_WEAPONIZED)
+ program_type = /datum/nanite_program/aggressive_replication
+
+/datum/design/nanites/meltdown
+ name = "Meltdown"
+ desc = "Causes an internal meltdown inside the nanites, causing internal burns inside the host as well as rapidly destroying the nanite population.\
+ Sets the nanites' safety threshold to 0 when activated."
+ id = "meltdown_nanites"
+ category = list(NANITES_CATEGORY_WEAPONIZED)
+ program_type = /datum/nanite_program/meltdown
+
+/datum/design/nanites/cryo
+ name = "Cryogenic Treatment"
+ desc = "The nanites rapidly skin heat through the host's skin, lowering their temperature."
+ id = "cryo_nanites"
+ category = list(NANITES_CATEGORY_WEAPONIZED)
+ program_type = /datum/nanite_program/cryo
+
+/datum/design/nanites/pyro
+ name = "Sub-Dermal Combustion"
+ desc = "The nanites cause buildup of flammable fluids under the host's skin, then ignites them."
+ id = "pyro_nanites"
+ category = list(NANITES_CATEGORY_WEAPONIZED)
+ program_type = /datum/nanite_program/pyro
+
+/datum/design/nanites/heart_stop
+ name = "Heart-Stopper"
+ desc = "Stops the host's heart when triggered; restarts it if triggered again."
+ id = "heartstop_nanites"
+ category = list(NANITES_CATEGORY_WEAPONIZED)
+ program_type = /datum/nanite_program/heart_stop
+
+/datum/design/nanites/explosive
+ name = "Chain Detonation"
+ desc = "Blows up all the nanites inside the host in a chain reaction when triggered."
+ id = "explosive_nanites"
+ category = list(NANITES_CATEGORY_WEAPONIZED)
+ program_type = /datum/nanite_program/explosive
+
+/datum/design/nanites/mind_control
+ name = "Mind Control"
+ desc = "The nanites imprint an absolute directive onto the host's brain while they're active."
+ id = "mindcontrol_nanites"
+ category = list(NANITES_CATEGORY_WEAPONIZED)
+ program_type = /datum/nanite_program/comm/mind_control
diff --git a/massmeta/features/nanites/code/items.dm b/massmeta/features/nanites/code/items.dm
new file mode 100644
index 0000000000000..37a29d713339f
--- /dev/null
+++ b/massmeta/features/nanites/code/items.dm
@@ -0,0 +1,149 @@
+/obj/item/storage/box/disks_nanite
+ name = "nanite program disks box"
+ illustration = "disk_kit"
+
+/obj/item/storage/box/disks_nanite/PopulateContents()
+ for(var/i in 1 to 7)
+ new /obj/item/disk/nanite_program(src)
+
+//Names are intentionally all the same - track your nanites, or use a hand labeler
+//This also means that you can give flesh melting nanites to your victims if you feel like it
+
+/obj/item/disk/nanite_program
+ name = "nanite program disk"
+ desc = "A disk capable of storing nanite programs. Can be customized using a Nanite Programming Console."
+ icon = 'massmeta/features/nanites/icons/disk.dmi'
+ icon_state = "nanite"
+ ///The program currently uploaded into the disk. If set to something, the disk will spawn with that program on mapload.
+ var/datum/nanite_program/program
+
+/obj/item/disk/nanite_program/Initialize(mapload)
+ . = ..()
+ if(program)
+ program = new program
+
+/obj/item/disk/nanite_program/aggressive_replication
+ program = /datum/nanite_program/aggressive_replication
+
+/obj/item/disk/nanite_program/metabolic_synthesis
+ program = /datum/nanite_program/metabolic_synthesis
+
+/obj/item/disk/nanite_program/viral
+ program = /datum/nanite_program/viral
+
+/obj/item/disk/nanite_program/meltdown
+ program = /datum/nanite_program/meltdown
+
+/obj/item/disk/nanite_program/relay
+ program = /datum/nanite_program/relay
+
+/obj/item/disk/nanite_program/emp
+ program = /datum/nanite_program/emp
+
+/obj/item/disk/nanite_program/spreading
+ program = /datum/nanite_program/spreading
+
+/obj/item/disk/nanite_program/regenerative
+ program = /datum/nanite_program/regenerative
+
+/obj/item/disk/nanite_program/regenerative_advanced
+ program = /datum/nanite_program/regenerative_advanced
+
+/obj/item/disk/nanite_program/temperature
+ program = /datum/nanite_program/temperature
+
+/obj/item/disk/nanite_program/purging
+ program = /datum/nanite_program/purging
+
+/obj/item/disk/nanite_program/purging_advanced
+ program = /datum/nanite_program/purging_advanced
+
+/obj/item/disk/nanite_program/brain_heal
+ program = /datum/nanite_program/brain_heal
+
+/obj/item/disk/nanite_program/brain_heal_advanced
+ program = /datum/nanite_program/brain_heal_advanced
+
+/obj/item/disk/nanite_program/blood_restoring
+ program = /datum/nanite_program/blood_restoring
+
+/obj/item/disk/nanite_program/repairing
+ program = /datum/nanite_program/repairing
+
+/obj/item/disk/nanite_program/nervous
+ program = /datum/nanite_program/nervous
+
+/obj/item/disk/nanite_program/hardening
+ program = /datum/nanite_program/hardening
+
+/obj/item/disk/nanite_program/coagulating
+ program = /datum/nanite_program/coagulating
+
+/obj/item/disk/nanite_program/necrotic
+ program = /datum/nanite_program/necrotic
+
+/obj/item/disk/nanite_program/brain_decay
+ program = /datum/nanite_program/brain_decay
+
+/obj/item/disk/nanite_program/pyro
+ program = /datum/nanite_program/pyro
+
+/obj/item/disk/nanite_program/cryo
+ program = /datum/nanite_program/cryo
+
+/obj/item/disk/nanite_program/toxic
+ program = /datum/nanite_program/toxic
+
+/obj/item/disk/nanite_program/suffocating
+ program = /datum/nanite_program/suffocating
+
+/obj/item/disk/nanite_program/heart_stop
+ program = /datum/nanite_program/heart_stop
+
+/obj/item/disk/nanite_program/explosive
+ program = /datum/nanite_program/explosive
+
+/obj/item/disk/nanite_program/shock
+ program = /datum/nanite_program/shocking
+
+/obj/item/disk/nanite_program/sleepy
+ program = /datum/nanite_program/sleepy
+
+/obj/item/disk/nanite_program/paralyzing
+ program = /datum/nanite_program/paralyzing
+
+/obj/item/disk/nanite_program/fake_death
+ program = /datum/nanite_program/fake_death
+
+/obj/item/disk/nanite_program/pacifying
+ program = /datum/nanite_program/pacifying
+
+/obj/item/disk/nanite_program/glitch
+ program = /datum/nanite_program/glitch
+
+/obj/item/disk/nanite_program/brain_misfire
+ program = /datum/nanite_program/brain_misfire
+
+/obj/item/disk/nanite_program/skin_decay
+ program = /datum/nanite_program/skin_decay
+
+/obj/item/disk/nanite_program/nerve_decay
+ program = /datum/nanite_program/nerve_decay
+
+/obj/item/disk/nanite_program/refractive
+ program = /datum/nanite_program/refractive
+
+/obj/item/disk/nanite_program/conductive
+ program = /datum/nanite_program/conductive
+
+/obj/item/disk/nanite_program/stun
+ program = /datum/nanite_program/stun
+
+/obj/item/disk/nanite_program/speech
+ program = /datum/nanite_program/comm/speech
+
+/obj/item/disk/nanite_program/voice
+ program = /datum/nanite_program/comm/voice
+
+/obj/item/disk/nanite_program/mind_control
+ program = /datum/nanite_program/comm/mind_control
diff --git a/massmeta/features/nanites/code/machines.dm b/massmeta/features/nanites/code/machines.dm
new file mode 100644
index 0000000000000..f0e4df998e88e
--- /dev/null
+++ b/massmeta/features/nanites/code/machines.dm
@@ -0,0 +1,15 @@
+/datum/design/board/nanite_chamber_control
+ name = "Computer Design (Nanite Chamber Control)"
+ desc = "Allows for the construction of circuit boards used to build a new nanite chamber control console."
+ id = "nanite_chamber_control"
+ build_path = /obj/item/circuitboard/computer/nanite_chamber_control
+ category = list(RND_CATEGORY_COMPUTER + RND_SUBCATEGORY_COMPUTER_RESEARCH)
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
+
+/datum/design/board/nanite_cloud_control
+ name = "Computer Design (Nanite Cloud Control)"
+ desc = "Allows for the construction of circuit boards used to build a new nanite cloud control console."
+ id = "nanite_cloud_control"
+ build_path = /obj/item/circuitboard/computer/nanite_cloud_controller
+ category = list(RND_CATEGORY_COMPUTER + RND_SUBCATEGORY_COMPUTER_RESEARCH)
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
diff --git a/massmeta/features/nanites/code/machines/nanite_chamber.dm b/massmeta/features/nanites/code/machines/nanite_chamber.dm
new file mode 100644
index 0000000000000..095703dc5ddc5
--- /dev/null
+++ b/massmeta/features/nanites/code/machines/nanite_chamber.dm
@@ -0,0 +1,221 @@
+#define NANITE_CHAMBER_BREAKOUT_TIME (2 MINUTES)
+
+/obj/machinery/nanite_chamber
+ name = "nanite chamber"
+ desc = "A device that can scan, reprogram, and inject nanites."
+ circuit = /obj/item/circuitboard/machine/nanite_chamber
+ icon = 'massmeta/features/nanites/icons/nanite_machines.dmi'
+ icon_state = "nanite_chamber"
+ base_icon_state = "nanite_chamber"
+ layer = ABOVE_WINDOW_LAYER
+ use_power = IDLE_POWER_USE
+ anchored = TRUE
+ density = TRUE
+ idle_power_usage = 50
+ active_power_usage = 300
+
+ ///The icon file used post-initialize, the default icon is used solely so it shows up in the R&D console.
+ var/chamber_icon = 'massmeta/features/nanites/icons/nanite_chamber.dmi'
+ ///The nanite chamber control machine we're synced to.
+ var/obj/machinery/computer/nanite_chamber_control/linked_console
+ ///The level of the scanning module installed in the nanite chamber.
+ var/scan_level
+ ///Boolean on whether we're currently locked, preventing the machine from being opened/closed.
+ var/locked = FALSE
+ ///Boolean on whether the machine is currently busy.
+ var/busy = FALSE
+ ///An icon state we're gonna set ourselves to, given to us by set_busy.
+ var/busy_icon_state
+ ///The message that will be displayed to the nanite controller, given by set_busy.
+ var/busy_message
+ ///The cooldown between messages telling a resisting player that they can't leave.
+ COOLDOWN_DECLARE(message_cooldown)
+
+/obj/machinery/nanite_chamber/Initialize(mapload)
+ occupant_typecache = GLOB.typecache_living
+ return ..()
+
+/obj/machinery/nanite_chamber/RefreshParts()
+ . = ..()
+ scan_level = 0
+ for(var/obj/item/stock_parts/scanning_module/scanning_mod in component_parts)
+ scan_level = scanning_mod.rating
+
+/obj/machinery/nanite_chamber/examine(mob/user)
+ . = ..()
+ if(in_range(user, src) || isobserver(user))
+ . += span_notice("The status display reads: Scanning module has been upgraded to level [scan_level].")
+
+/obj/machinery/nanite_chamber/proc/set_busy(status, message, working_icon)
+ busy = status
+ busy_message = message
+ busy_icon_state = working_icon
+ update_appearance(UPDATE_ICON)
+
+/obj/machinery/nanite_chamber/proc/set_safety(threshold)
+ if(!occupant)
+ return
+ SEND_SIGNAL(occupant, COMSIG_NANITE_SET_SAFETY, threshold)
+
+/obj/machinery/nanite_chamber/proc/set_cloud(cloud_id)
+ if(!occupant)
+ return
+ SEND_SIGNAL(occupant, COMSIG_NANITE_SET_CLOUD, cloud_id)
+
+/obj/machinery/nanite_chamber/proc/inject_nanites()
+ if(machine_stat & (NOPOWER|BROKEN))
+ return
+ if((machine_stat & MAINT) || panel_open)
+ return
+ if(!occupant || busy)
+ return
+
+ var/locked_state = locked
+ locked = TRUE
+
+ playsound(src, 'massmeta/features/nanites/sound/nanite_install.wav', 50)
+ set_busy(TRUE, "Initializing injection protocol...", "[initial(icon_state)]_raising")
+ addtimer(CALLBACK(src, PROC_REF(set_busy), TRUE, "Analyzing host bio-structure...", "[initial(icon_state)]_active"), 2 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(set_busy), TRUE, "Priming nanites...", "[initial(icon_state)]_active"), 4 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(set_busy), TRUE, "Injecting...", "[initial(icon_state)]_active"), 7 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(set_busy), TRUE, "Activating nanites...", "[initial(icon_state)]_falling"), 9 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(complete_injection), locked_state), 11 SECONDS)
+
+/obj/machinery/nanite_chamber/proc/complete_injection(locked_state)
+ //TODO MACHINE DING
+ locked = locked_state
+ set_busy(FALSE)
+ if(!occupant || !linked_console.linked_techweb)
+ return
+ occupant.AddComponent(/datum/component/nanites, linked_console.linked_techweb)
+
+/obj/machinery/nanite_chamber/proc/remove_nanites()
+ if(machine_stat & (NOPOWER|BROKEN))
+ return
+ if((machine_stat & MAINT) || panel_open)
+ return
+ if(!occupant || busy)
+ return
+
+ var/locked_state = locked
+ locked = TRUE
+
+ playsound(src, 'massmeta/features/nanites/sound/nanite_install_short.mp3', 50)
+ set_busy(TRUE, "Initializing cleanup protocol...", "[initial(icon_state)]_raising")
+ addtimer(CALLBACK(src, PROC_REF(set_busy), TRUE, "Analyzing host bio-structure...", "[initial(icon_state)]_active"), 2 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(set_busy), TRUE, "Pinging nanites...", "[initial(icon_state)]_active"), 3 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(set_busy), TRUE, "Initiating graceful self-destruct sequence...", "[initial(icon_state)]_active"), 5 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(set_busy), TRUE, "Removing debris...", "[initial(icon_state)]_falling"), 7 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(complete_removal), locked_state), 9 SECONDS)
+
+/obj/machinery/nanite_chamber/proc/complete_removal(locked_state)
+ //TODO MACHINE DING
+ locked = locked_state
+ set_busy(FALSE)
+ if(!occupant)
+ return
+ SEND_SIGNAL(occupant, COMSIG_NANITE_DELETE)
+
+/obj/machinery/nanite_chamber/update_icon(updates=ALL)
+ icon = chamber_icon
+ return ..()
+
+/obj/machinery/nanite_chamber/update_icon_state()
+ . = ..()
+ if(!occupant)
+ icon_state = "[base_icon_state][state_open ? "_open" : ""]"
+ return
+ if(busy)
+ icon_state = busy_icon_state
+ else
+ icon_state = "[base_icon_state]_occupied"
+
+/obj/machinery/nanite_chamber/update_overlays()
+ . = ..()
+ if((machine_stat & MAINT) || panel_open)
+ . += "maint"
+ return .
+ if(machine_stat & (NOPOWER|BROKEN))
+ return .
+ if(!busy && !locked)
+ . += "green"
+ return .
+ . += "red"
+ if(locked)
+ . += "bolted"
+
+/obj/machinery/nanite_chamber/proc/toggle_open(mob/user)
+ if(panel_open)
+ balloon_alert(user, "panel open!")
+ return
+ if(state_open)
+ close_machine()
+ return
+ if(locked)
+ balloon_alert(user, "bolts locked down!")
+ return
+ open_machine()
+
+/obj/machinery/nanite_chamber/container_resist_act(mob/living/user)
+ if(!locked)
+ open_machine()
+ return
+ if(busy)
+ return
+ user.changeNext_move(CLICK_CD_BREAKOUT)
+ user.last_special = world.time + CLICK_CD_BREAKOUT
+ user.visible_message(
+ span_notice("You see [user] kicking against the door of [src]!"),
+ span_notice("You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(NANITE_CHAMBER_BREAKOUT_TIME)].)"),
+ span_hear("You hear a metallic creaking from [src]."),
+ )
+ if(!do_after(user, NANITE_CHAMBER_BREAKOUT_TIME, target = src))
+ return
+ if(!user || user.stat != CONSCIOUS || user.loc != src || state_open || !locked || busy)
+ return
+ locked = FALSE
+ user.visible_message(
+ span_warning("[user] successfully broke out of [src]!"),
+ span_notice("You successfully break out of [src]!"))
+ open_machine()
+
+/obj/machinery/nanite_chamber/close_machine(atom/movable/target, density_to_set = TRUE)
+ if(!state_open)
+ return FALSE
+ playsound(src, 'massmeta/features/nanites/sound/nanite_chamber.wav', 40)
+ return ..()
+
+/obj/machinery/nanite_chamber/open_machine(drop = TRUE, density_to_set = FALSE)
+ if(state_open)
+ return FALSE
+ playsound(src, 'massmeta/features/nanites/sound/nanite_chamber.wav', 40)
+ return ..()
+
+/obj/machinery/nanite_chamber/relaymove(mob/living/user, direction)
+ if((user.stat < HARD_CRIT) && !locked)
+ open_machine()
+ return
+ if(COOLDOWN_FINISHED(src, message_cooldown))
+ COOLDOWN_START(src, message_cooldown, 5 SECONDS)
+ balloon_alert(user, "door won't budge!")
+
+/obj/machinery/nanite_chamber/crowbar_act(mob/living/user, obj/item/tool)
+ if(default_pry_open(tool) || default_deconstruction_crowbar(tool))
+ return ITEM_INTERACT_SUCCESS
+ return ..()
+
+/obj/machinery/nanite_chamber/screwdriver_act(mob/living/user, obj/item/tool)
+ if(!occupant && default_deconstruction_screwdriver(user, icon_state, icon_state, tool))
+ update_appearance()
+ return ITEM_INTERACT_SUCCESS
+ return ..()
+
+/obj/machinery/nanite_chamber/interact(mob/user)
+ toggle_open(user)
+
+/obj/machinery/nanite_chamber/mouse_drop_receive(mob/target, mob/user, params)
+ if(!user.can_perform_action(src, FORBID_TELEKINESIS_REACH) || !Adjacent(target) || !user.Adjacent(target) || !(can_be_occupant(target)))
+ return
+ if(close_machine(target))
+ log_combat(user, target, "inserted", null, "into [src].")
+ add_fingerprint(user)
diff --git a/massmeta/features/nanites/code/machines/nanite_chamber_control.dm b/massmeta/features/nanites/code/machines/nanite_chamber_control.dm
new file mode 100644
index 0000000000000..c1d8a7c3f7023
--- /dev/null
+++ b/massmeta/features/nanites/code/machines/nanite_chamber_control.dm
@@ -0,0 +1,123 @@
+/obj/machinery/computer/nanite_chamber_control
+ name = "nanite chamber control console"
+ desc = "Controls a connected nanite chamber. Can inoculate and destroy nanites or analyze existing nanite swarms within patients."
+ icon = 'massmeta/features/nanites/icons/computer.dmi'
+ icon_screen = "nanite_chamber_control"
+ icon_keyboard = null
+ circuit = /obj/item/circuitboard/computer/nanite_chamber_control
+
+ ///The nanite chamber we're connected to, that we use to scan people and modify nanites.
+ var/obj/machinery/nanite_chamber/chamber
+ ///The techweb that hosts the nanites we're injecting into people.
+ var/datum/techweb/linked_techweb
+
+/obj/machinery/computer/nanite_chamber_control/post_machine_initialize()
+ . = ..()
+ find_chamber()
+ if(!CONFIG_GET(flag/no_default_techweb_link) && !linked_techweb)
+ CONNECT_TO_RND_SERVER_ROUNDSTART(linked_techweb, src)
+
+/obj/machinery/computer/nanite_chamber_control/Destroy()
+ linked_techweb = null
+ return ..()
+
+/obj/machinery/computer/nanite_chamber_control/interact()
+ if(!chamber)
+ find_chamber()
+ return ..()
+
+/obj/machinery/computer/nanite_chamber_control/multitool_act(mob/living/user, obj/item/multitool/tool)
+ if(!QDELETED(tool.buffer) && istype(tool.buffer, /datum/techweb))
+ linked_techweb = tool.buffer
+ return TRUE
+
+/obj/machinery/computer/nanite_chamber_control/ui_interact(mob/user, datum/tgui/ui)
+ . = ..()
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "NaniteChamberControl", name)
+ ui.open()
+
+/obj/machinery/computer/nanite_chamber_control/ui_data()
+ var/list/data = list()
+
+ if(!linked_techweb)
+ data["status_msg"] = "No techweb detected."
+ return data
+
+ if(!chamber)
+ data["status_msg"] = "No chamber detected."
+ return data
+
+ if(!chamber.occupant)
+ data["status_msg"] = "No occupant detected."
+ return data
+
+ var/mob/living/person_inside = chamber.occupant
+
+ if(!(person_inside.mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD)))
+ data["status_msg"] = "Occupant not compatible with nanites."
+ return data
+
+ if(chamber.busy)
+ data["status_msg"] = chamber.busy_message
+ return data
+
+ data["has_nanites"] = FALSE
+ data["status_msg"] = null
+ data["scan_level"] = chamber.scan_level
+ data["locked"] = chamber.locked
+ data["occupant_name"] = chamber.occupant.name
+
+ SEND_SIGNAL(person_inside, COMSIG_NANITE_UI_DATA, data, chamber.scan_level)
+
+ return data
+
+/obj/machinery/computer/nanite_chamber_control/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("toggle_lock")
+ chamber.locked = !chamber.locked
+ chamber.update_appearance(UPDATE_ICON)
+ return TRUE
+ if("set_safety")
+ var/threshold = text2num(params["value"])
+ if(!isnull(threshold))
+ chamber.set_safety(clamp(round(threshold, 1), 0, 500))
+ playsound(src, "terminal_type", 25, FALSE)
+ log_game("[chamber.occupant]'s nanites' safety threshold was set to [threshold] by [key_name(usr)] via [src] at [AREACOORD(src)].")
+ return TRUE
+ if("set_cloud")
+ var/cloud_id = text2num(params["value"])
+ if(!isnull(cloud_id))
+ chamber.set_cloud(clamp(round(cloud_id, 1), 0, 100))
+ playsound(src, "terminal_type", 25, FALSE)
+ log_game("[chamber.occupant]'s nanites' cloud id was set to [cloud_id] by [key_name(usr)] via [src] at [AREACOORD(src)].")
+ return TRUE
+ if("connect_chamber")
+ find_chamber()
+ return TRUE
+ if("remove_nanites")
+ playsound(src, 'sound/machines/terminal_prompt.ogg', 25, FALSE)
+ chamber.remove_nanites()
+ log_combat(usr, chamber.occupant, "cleared nanites from", null, "via [src]")
+ log_game("[chamber.occupant]'s nanites were cleared by [key_name(usr)] via [src] at [AREACOORD(src)].")
+ return TRUE
+ if("nanite_injection")
+ playsound(src, 'sound/machines/terminal_prompt.ogg', 25, FALSE)
+ chamber.inject_nanites()
+ log_combat(usr, chamber.occupant, "injected", null, "with nanites via [src]")
+ log_game("[chamber.occupant] was injected with nanites by [key_name(usr)] via [src] at [AREACOORD(src)].")
+ return TRUE
+
+///Looks in all directions for a nanite chamber to sync to.
+/obj/machinery/computer/nanite_chamber_control/proc/find_chamber()
+ for(var/direction in GLOB.cardinals)
+ var/found_chamber = locate(/obj/machinery/nanite_chamber, get_step(src, direction))
+ if(!found_chamber)
+ continue
+ var/obj/machinery/nanite_chamber/nanite_chamber = found_chamber
+ chamber = nanite_chamber
+ nanite_chamber.linked_console = src
diff --git a/massmeta/features/nanites/code/machines/nanite_cloud_control.dm b/massmeta/features/nanites/code/machines/nanite_cloud_control.dm
new file mode 100644
index 0000000000000..326633b2fe97c
--- /dev/null
+++ b/massmeta/features/nanites/code/machines/nanite_cloud_control.dm
@@ -0,0 +1,267 @@
+/obj/machinery/computer/nanite_cloud_controller
+ name = "nanite cloud controller"
+ desc = "Stores and controls nanite cloud backups."
+ icon = 'massmeta/features/nanites/icons/nanite_machines.dmi'
+ icon_state = "nanite_cloud_controller"
+ circuit = /obj/item/circuitboard/computer/nanite_cloud_controller
+ brightness_on = FALSE
+ icon_keyboard = null
+ icon_screen = null
+
+ ///The disk currently inserted into the cloud control.
+ var/obj/item/disk/nanite_program/disk
+ ///The list of all cloud backups that we are a host to.
+ var/list/datum/nanite_cloud_backup/cloud_backups = list()
+ ///The current page we're viewing, 0 is the main menu, all others is their respective cloud backup ID.
+ var/current_view = 0
+ ///The currently set Backup ID, if we create a new cloud backup it will take this ID if possible.
+ var/new_backup_id = 1
+ ///The techweb we're linked to, required for the machine to work.
+ var/datum/techweb/linked_techweb
+
+/obj/machinery/computer/nanite_cloud_controller/Destroy()
+ QDEL_LIST(cloud_backups) //rip backups
+ linked_techweb = null
+ eject()
+ return ..()
+
+/obj/machinery/computer/nanite_cloud_controller/post_machine_initialize()
+ . = ..()
+ if(!CONFIG_GET(flag/no_default_techweb_link) && !linked_techweb)
+ CONNECT_TO_RND_SERVER_ROUNDSTART(linked_techweb, src)
+
+/obj/machinery/computer/nanite_cloud_controller/attackby(obj/item/weapon, mob/user, params)
+ if(!istype(weapon, /obj/item/disk/nanite_program))
+ return ..()
+ if(!user.transferItemToLoc(weapon, src))
+ return
+ if(disk)
+ balloon_alert(user, "disk swapped")
+ eject(user)
+ else
+ balloon_alert(user, "disk inserted")
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ disk = weapon
+
+/obj/machinery/computer/nanite_cloud_controller/multitool_act(mob/living/user, obj/item/multitool/tool)
+ if(!QDELETED(tool.buffer) && istype(tool.buffer, /datum/techweb))
+ linked_techweb = tool.buffer
+ return TRUE
+
+/obj/machinery/computer/nanite_cloud_controller/attack_hand_secondary(mob/user, list/modifiers)
+ if(disk && user.can_perform_action(src, ALLOW_SILICON_REACH))
+ balloon_alert(user, "disk ejected")
+ eject(user)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ return ..()
+
+/obj/machinery/computer/nanite_cloud_controller/proc/eject(mob/living/user)
+ if(!disk || !user)
+ return
+ if(!istype(user) || !Adjacent(user) ||!user.put_in_active_hand(disk))
+ disk.forceMove(drop_location())
+ disk = null
+
+/obj/machinery/computer/nanite_cloud_controller/proc/get_backup(cloud_id)
+ for(var/datum/nanite_cloud_backup/backup as anything in cloud_backups)
+ if(backup.cloud_id == cloud_id)
+ return backup
+
+/obj/machinery/computer/nanite_cloud_controller/proc/generate_backup(cloud_id, mob/user)
+ if(SSnanites.get_cloud_backup(cloud_id, TRUE))
+ to_chat(user, span_warning("Cloud ID already registered."))
+ return
+
+ var/datum/nanite_cloud_backup/backup = new(src, cloud_id)
+ var/datum/component/nanites/cloud_copy = backup.AddComponent(/datum/component/nanites, linked_techweb)
+ backup.set_nanites(cloud_copy)
+ log_game("[key_name(user)] created a new nanite cloud backup with id #[cloud_id]")
+
+/obj/machinery/computer/nanite_cloud_controller/ui_interact(mob/user, datum/tgui/ui)
+ . = ..()
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "NaniteCloudControl", name)
+ ui.open()
+
+/obj/machinery/computer/nanite_cloud_controller/ui_data()
+ var/list/data = list()
+
+ data["can_rule"] = FALSE
+ if(disk)
+ data["has_disk"] = TRUE
+ var/list/disk_data = list()
+ var/datum/nanite_program/current_program = disk.program
+ if(current_program)
+ data["has_program"] = TRUE
+ disk_data["name"] = current_program.name
+ disk_data["desc"] = current_program.desc
+ disk_data["use_rate"] = current_program.use_rate
+ disk_data["can_trigger"] = current_program.can_trigger
+ disk_data["trigger_cost"] = current_program.trigger_cost
+ disk_data["trigger_cooldown"] = current_program.trigger_cooldown / 10
+
+ disk_data["activated"] = current_program.activated
+ disk_data["activation_code"] = current_program.activation_code
+ disk_data["deactivation_code"] = current_program.deactivation_code
+ disk_data["kill_code"] = current_program.kill_code
+ disk_data["trigger_code"] = current_program.trigger_code
+ disk_data["timer_restart"] = current_program.timer_restart / 10
+ disk_data["timer_shutdown"] = current_program.timer_shutdown / 10
+ disk_data["timer_trigger"] = current_program.timer_trigger / 10
+ disk_data["timer_trigger_delay"] = current_program.timer_trigger_delay / 10
+
+ var/list/extra_settings = current_program.get_extra_settings_frontend()
+ disk_data["extra_settings"] = extra_settings
+ if(LAZYLEN(extra_settings))
+ disk_data["has_extra_settings"] = TRUE
+ if(istype(current_program, /datum/nanite_program/sensor))
+ var/datum/nanite_program/sensor/sensor = current_program
+ data["can_rule"] = sensor.can_rule
+ data["disk_data"] = disk_data
+ else
+ data["has_disk"] = FALSE
+
+ data["new_backup_id"] = new_backup_id
+
+ data["current_view"] = current_view
+ if(current_view)
+ var/datum/nanite_cloud_backup/backup = get_backup(current_view)
+ if(backup)
+ var/datum/component/nanites/nanites = backup.nanites
+ data["cloud_backup"] = TRUE
+ var/list/cloud_programs = list()
+ var/id = 1
+ for(var/datum/nanite_program/cloud_program as anything in nanites.programs)
+ var/list/cloud_program_data = list()
+ cloud_program_data["name"] = cloud_program.name
+ cloud_program_data["desc"] = cloud_program.desc
+ cloud_program_data["id"] = id
+ cloud_program_data["use_rate"] = cloud_program.use_rate
+ cloud_program_data["can_trigger"] = cloud_program.can_trigger
+ cloud_program_data["trigger_cost"] = cloud_program.trigger_cost
+ cloud_program_data["trigger_cooldown"] = cloud_program.trigger_cooldown / 10
+ cloud_program_data["activated"] = cloud_program.activated
+ cloud_program_data["timer_restart"] = cloud_program.timer_restart / 10
+ cloud_program_data["timer_shutdown"] = cloud_program.timer_shutdown / 10
+ cloud_program_data["timer_trigger"] = cloud_program.timer_trigger / 10
+ cloud_program_data["timer_trigger_delay"] = cloud_program.timer_trigger_delay / 10
+
+ cloud_program_data["activation_code"] = cloud_program.activation_code
+ cloud_program_data["deactivation_code"] = cloud_program.deactivation_code
+ cloud_program_data["kill_code"] = cloud_program.kill_code
+ cloud_program_data["trigger_code"] = cloud_program.trigger_code
+ var/list/rules = list()
+ var/rule_id = 1
+ for(var/datum/nanite_rule/nanite_rule as anything in cloud_program.rules)
+ var/list/rule = list()
+ rule["display"] = nanite_rule.display()
+ rule["program_id"] = id
+ rule["id"] = rule_id
+ rules += list(rule)
+ rule_id++
+ cloud_program_data["rules"] = rules
+ if(LAZYLEN(rules))
+ cloud_program_data["has_rules"] = TRUE
+ cloud_program_data["all_rules_required"] = cloud_program.all_rules_required
+
+ var/list/extra_settings = cloud_program.get_extra_settings_frontend()
+ cloud_program_data["extra_settings"] = extra_settings
+ if(LAZYLEN(extra_settings))
+ cloud_program_data["has_extra_settings"] = TRUE
+ id++
+ cloud_programs += list(cloud_program_data)
+ data["cloud_programs"] = cloud_programs
+ else
+ var/list/backup_list = list()
+ for(var/datum/nanite_cloud_backup/backup as anything in cloud_backups)
+ var/list/cloud_backup = list()
+ cloud_backup["cloud_id"] = backup.cloud_id
+ backup_list += list(cloud_backup)
+ data["cloud_backups"] = backup_list
+ return data
+
+/obj/machinery/computer/nanite_cloud_controller/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("eject")
+ eject(usr)
+ return TRUE
+ if("set_view")
+ current_view = text2num(params["view"])
+ return TRUE
+ if("update_new_backup_value")
+ var/backup_value = text2num(params["value"])
+ new_backup_id = backup_value
+ return TRUE
+ if("create_backup")
+ var/cloud_id = new_backup_id
+ if(!isnull(cloud_id))
+ playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
+ cloud_id = clamp(round(cloud_id, 1),1,100)
+ generate_backup(cloud_id, usr)
+ return TRUE
+ if("delete_backup")
+ var/datum/nanite_cloud_backup/backup = get_backup(current_view)
+ if(!backup)
+ return TRUE
+ playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
+ qdel(backup)
+ log_game("[key_name(usr)] deleted the nanite cloud backup #[current_view]")
+ return TRUE
+ if("upload_program")
+ if(disk && disk.program)
+ var/datum/nanite_cloud_backup/backup = get_backup(current_view)
+ if(!backup)
+ return TRUE
+ playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
+ var/datum/component/nanites/nanites = backup.nanites
+ nanites.add_program(null, disk.program.copy())
+ log_game("[key_name(usr)] uploaded program [disk.program.name] to cloud #[current_view]")
+ return TRUE
+ if("remove_program")
+ var/datum/nanite_cloud_backup/backup = get_backup(current_view)
+ if(!backup)
+ return TRUE
+ playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
+ var/datum/component/nanites/nanites = backup.nanites
+ var/datum/nanite_program/cloud_program = nanites.programs[text2num(params["program_id"])]
+ log_game("[key_name(usr)] deleted program [cloud_program.name] from cloud #[current_view]")
+ qdel(cloud_program)
+ return TRUE
+ if("add_rule")
+ if(disk && disk.program && istype(disk.program, /datum/nanite_program/sensor))
+ var/datum/nanite_program/sensor/rule_template = disk.program
+ if(!rule_template.can_rule)
+ return
+ var/datum/nanite_cloud_backup/backup = get_backup(current_view)
+ if(backup)
+ playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
+ var/datum/component/nanites/nanites = backup.nanites
+ var/datum/nanite_program/ruled_program = nanites.programs[text2num(params["program_id"])]
+ var/datum/nanite_rule/rule = rule_template.make_rule(ruled_program)
+ log_game("[key_name(usr)] added rule [rule.display()] to program [ruled_program.name] in cloud #[current_view]")
+ return TRUE
+ if("remove_rule")
+ var/datum/nanite_cloud_backup/backup = get_backup(current_view)
+ if(!backup)
+ return TRUE
+ playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
+ var/datum/component/nanites/nanites = backup.nanites
+ var/datum/nanite_program/ruleless_program = nanites.programs[text2num(params["program_id"])]
+ var/datum/nanite_rule/rule = ruleless_program.rules[text2num(params["rule_id"])]
+ rule.remove()
+ log_game("[key_name(usr)] removed rule [rule.display()] from program [ruleless_program.name] in cloud #[current_view]")
+ return TRUE
+ if("toggle_rule_logic")
+ var/datum/nanite_cloud_backup/backup = get_backup(current_view)
+ if(!backup)
+ return TRUE
+ playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
+ var/datum/component/nanites/nanites = backup.nanites
+ var/datum/nanite_program/logical_program = nanites.programs[text2num(params["program_id"])]
+ logical_program.all_rules_required = !logical_program.all_rules_required
+ log_game("[key_name(usr)] edited rule logic for program [logical_program.name] into [logical_program.all_rules_required ? "All" : "Any"] in cloud #[current_view]")
+ return TRUE
diff --git a/massmeta/features/nanites/code/machines/nanite_program_hub.dm b/massmeta/features/nanites/code/machines/nanite_program_hub.dm
new file mode 100644
index 0000000000000..f5339a8f6610e
--- /dev/null
+++ b/massmeta/features/nanites/code/machines/nanite_program_hub.dm
@@ -0,0 +1,200 @@
+/obj/machinery/nanite_program_hub
+ name = "nanite program hub"
+ desc = "Compiles nanite programs from the techweb servers and downloads them into disks."
+ icon = 'massmeta/features/nanites/icons/nanite_machines.dmi'
+ icon_state = "nanite_program_hub"
+ use_power = IDLE_POWER_USE
+ anchored = TRUE
+ density = TRUE
+ circuit = /obj/item/circuitboard/machine/nanite_program_hub
+
+ ///Boolean on whether the UI should give a detailed view of everything.
+ var/detail_view = TRUE
+ ///The disk currently inserted into the machine, that we upload programs onto.
+ var/obj/item/disk/nanite_program/inserted_disk
+ ///The techweb we're connected to, and get designs from.
+ var/datum/techweb/linked_techweb
+ ///List of all unlocked nanite designs, cached to only state when you receive a new one.
+ var/list/datum/design/nanites/cached_designs = list()
+
+/obj/machinery/nanite_program_hub/Destroy()
+ linked_techweb = null
+ return ..()
+
+/obj/machinery/nanite_program_hub/post_machine_initialize()
+ . = ..()
+ if(!CONFIG_GET(flag/no_default_techweb_link) && !linked_techweb)
+ CONNECT_TO_RND_SERVER_ROUNDSTART(linked_techweb, src)
+ if(linked_techweb)
+ on_connected_techweb()
+
+/obj/machinery/nanite_program_hub/proc/connect_techweb(datum/techweb/new_techweb)
+ if(linked_techweb)
+ UnregisterSignal(linked_techweb, list(COMSIG_TECHWEB_ADD_DESIGN))
+ linked_techweb = new_techweb
+ if(!isnull(linked_techweb))
+ on_connected_techweb()
+
+/obj/machinery/nanite_program_hub/proc/on_connected_techweb()
+ for (var/researched_design_id in linked_techweb.researched_designs)
+ var/datum/design/nanites/design = SSresearch.techweb_design_by_id(researched_design_id)
+ if (!ispath(design))
+ continue
+
+ cached_designs[design.program_type] = design.id
+
+ RegisterSignal(linked_techweb, COMSIG_TECHWEB_ADD_DESIGN, PROC_REF(on_research))
+
+/obj/machinery/nanite_program_hub/proc/on_research(datum/source, datum/design/nanites/added_design, custom)
+ SIGNAL_HANDLER
+ // We're probably going to get more than one update (design) at a time, so batch them together.
+ addtimer(CALLBACK(src, PROC_REF(update_menu_tech)), 2 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
+
+/**
+ * Updates the `final_sets` and `buildable_parts` for the current mecha fabricator.
+ */
+/obj/machinery/nanite_program_hub/proc/update_menu_tech()
+ var/previous_design_count = cached_designs.len
+
+ cached_designs.Cut()
+ for(var/v in linked_techweb.researched_designs)
+ var/datum/design/nanites/design = SSresearch.techweb_design_by_id(v)
+
+ if(istype(design))
+ cached_designs |= design
+
+ var/design_delta = cached_designs.len - previous_design_count
+
+ if(design_delta > 0)
+ say("Received [design_delta] new design[design_delta == 1 ? "" : "s"].")
+ playsound(src, 'sound/machines/twobeep_high.ogg', 50, TRUE)
+
+ update_static_data_for_all_viewers()
+
+/obj/machinery/nanite_program_hub/attackby(obj/item/weapon, mob/user, params)
+ if(!istype(weapon, /obj/item/disk/nanite_program))
+ return ..()
+ if(!user.transferItemToLoc(weapon, src))
+ return
+ if(inserted_disk)
+ balloon_alert(user, "disk swapped")
+ eject(user)
+ else
+ balloon_alert(user, "disk inserted")
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ inserted_disk = weapon
+
+/obj/machinery/nanite_program_hub/crowbar_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_crowbar(tool))
+ return ITEM_INTERACT_SUCCESS
+ return ..()
+
+/obj/machinery/nanite_program_hub/screwdriver_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool))
+ return ITEM_INTERACT_SUCCESS
+ return ..()
+
+/obj/machinery/nanite_program_hub/multitool_act(mob/living/user, obj/item/multitool/tool)
+ if(!QDELETED(tool.buffer) && istype(tool.buffer, /datum/techweb))
+ connect_techweb(tool.buffer)
+ return TRUE
+
+/obj/machinery/nanite_program_hub/proc/eject(mob/living/user)
+ if(!inserted_disk)
+ return
+ if(!istype(user) || !Adjacent(user) || !user.put_in_active_hand(inserted_disk))
+ inserted_disk.forceMove(drop_location())
+ inserted_disk = null
+
+/obj/machinery/nanite_program_hub/attack_hand_secondary(mob/user, list/modifiers)
+ if(inserted_disk && user.can_perform_action(src, ALLOW_SILICON_REACH))
+ balloon_alert(user, "disk ejected")
+ eject(user)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ return ..()
+
+/obj/machinery/nanite_program_hub/ui_interact(mob/user, datum/tgui/ui)
+ if(!linked_techweb)
+ visible_message("Warning: no linked server!")
+ SStgui.close_uis(src)
+ return
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "NaniteProgramHub", name)
+ ui.open()
+
+/obj/machinery/nanite_program_hub/ui_data()
+ var/list/data = list()
+ if(inserted_disk)
+ data["has_disk"] = TRUE
+ var/list/disk_data = list()
+ var/datum/nanite_program/P = inserted_disk.program
+ if(P)
+ data["has_program"] = TRUE
+ disk_data["name"] = P.name
+ disk_data["desc"] = P.desc
+ data["disk"] = disk_data
+ else
+ data["has_disk"] = FALSE
+
+ data["detail_view"] = detail_view
+
+ return data
+
+/obj/machinery/nanite_program_hub/ui_static_data(mob/user)
+ var/list/data = list()
+ data["programs"] = list()
+ data["categories"] = list()
+ for(var/i in linked_techweb.researched_designs)
+ var/datum/design/nanites/D = SSresearch.techweb_design_by_id(i)
+ if(!istype(D))
+ continue
+ var/cat_name = D.category[1] //just put them in the first category fuck it
+ if(!(cat_name in data["categories"]))
+ data["categories"] += cat_name
+ if(isnull(data["programs"][cat_name]))
+ data["programs"][cat_name] = list()
+ var/list/program_design = list()
+ program_design["id"] = D.id
+ program_design["name"] = D.name
+ program_design["desc"] = D.desc
+ data["programs"][cat_name] += list(program_design)
+
+ if(!length(data["programs"]))
+ data["programs"] = null
+
+ return data
+
+/obj/machinery/nanite_program_hub/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("eject")
+ eject(usr)
+ return TRUE
+ if("download")
+ if(!inserted_disk)
+ return
+ var/datum/design/nanites/downloaded = linked_techweb.isDesignResearchedID(params["program_id"]) //check if it's a valid design
+ if(!istype(downloaded))
+ return
+ if(inserted_disk.program)
+ qdel(inserted_disk.program)
+ inserted_disk.program = new downloaded.program_type
+ inserted_disk.name = "[initial(inserted_disk.name)] \[[inserted_disk.program.name]\]"
+ playsound(src, 'sound/machines/terminal_prompt.ogg', 25, FALSE)
+ return TRUE
+ if("refresh")
+ update_static_data(usr)
+ return TRUE
+ if("toggle_details")
+ detail_view = !detail_view
+ return TRUE
+ if("clear")
+ if(inserted_disk && inserted_disk.program)
+ qdel(inserted_disk.program)
+ inserted_disk.program = null
+ inserted_disk.name = initial(inserted_disk.name)
+ playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 25, FALSE)
+ return TRUE
diff --git a/massmeta/features/nanites/code/machines/nanite_programmer.dm b/massmeta/features/nanites/code/machines/nanite_programmer.dm
new file mode 100644
index 0000000000000..246921944b37c
--- /dev/null
+++ b/massmeta/features/nanites/code/machines/nanite_programmer.dm
@@ -0,0 +1,167 @@
+/obj/machinery/nanite_programmer
+ name = "nanite programmer"
+ desc = "A device that can edit nanite program disks to adjust their functionality."
+ icon = 'massmeta/features/nanites/icons/nanite_machines.dmi'
+ icon_state = "nanite_programmer"
+ use_power = IDLE_POWER_USE
+ anchored = TRUE
+ density = TRUE
+ circuit = /obj/item/circuitboard/machine/nanite_programmer
+
+ ///The delay between 'when you code it' replies.
+ COOLDOWN_DECLARE(wyci_delay)
+ ///The disk inserted into the machine that we hold and get the program of.
+ var/obj/item/disk/nanite_program/disk
+ ///The program on the nanite disk that we are currently editing.
+ var/datum/nanite_program/program
+
+/obj/machinery/nanite_programmer/Initialize(mapload)
+ . = ..()
+ become_hearing_sensitive()
+
+/obj/machinery/nanite_programmer/attackby(obj/item/weapon, mob/user, params)
+ if(!istype(weapon, /obj/item/disk/nanite_program))
+ return ..()
+ if(!user.transferItemToLoc(weapon, src))
+ return
+ if(disk)
+ balloon_alert(user, "disk swapped")
+ eject(user)
+ else
+ balloon_alert(user, "disk inserted")
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ disk = weapon
+ program = disk.program
+
+/obj/machinery/nanite_programmer/attack_hand_secondary(mob/user, list/modifiers)
+ if(disk && user.can_perform_action(src, ALLOW_SILICON_REACH))
+ balloon_alert(user, "disk ejected")
+ eject(user)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ return ..()
+
+/obj/machinery/nanite_programmer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range=0)
+ . = ..()
+ if(!COOLDOWN_FINISHED(src, wyci_delay))
+ return
+ var/static/regex/when = regex("(?:^\\W*when|when\\W*$)", "i") //starts or ends with when
+ if(findtext(raw_message, when) && !istype(speaker, /obj/machinery/nanite_programmer))
+ say("When you code it!!")
+ COOLDOWN_START(src, wyci_delay, 5 SECONDS)
+
+/obj/machinery/nanite_programmer/crowbar_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_crowbar(tool))
+ return ITEM_INTERACT_SUCCESS
+ return ..()
+
+/obj/machinery/nanite_programmer/screwdriver_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool))
+ return ITEM_INTERACT_SUCCESS
+ return ..()
+
+/obj/machinery/nanite_programmer/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "NaniteProgrammer", name)
+ ui.open()
+
+/obj/machinery/nanite_programmer/ui_data()
+ var/list/data = list()
+ data["has_disk"] = istype(disk)
+ data["has_program"] = istype(program)
+ if(program)
+ data["name"] = program.name
+ data["desc"] = program.desc
+ data["use_rate"] = program.use_rate
+ data["can_trigger"] = program.can_trigger
+ data["trigger_cost"] = program.trigger_cost
+ data["trigger_cooldown"] = program.trigger_cooldown / 10
+
+ data["activated"] = program.activated
+ data["activation_code"] = program.activation_code
+ data["deactivation_code"] = program.deactivation_code
+ data["kill_code"] = program.kill_code
+ data["trigger_code"] = program.trigger_code
+ data["timer_restart"] = program.timer_restart / 10
+ data["timer_shutdown"] = program.timer_shutdown / 10
+ data["timer_trigger"] = program.timer_trigger / 10
+ data["timer_trigger_delay"] = program.timer_trigger_delay / 10
+
+ var/list/extra_settings = program.get_extra_settings_frontend()
+ data["extra_settings"] = extra_settings
+ if(LAZYLEN(extra_settings))
+ data["has_extra_settings"] = TRUE
+
+ return data
+
+/obj/machinery/nanite_programmer/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("eject")
+ eject(usr)
+ return TRUE
+ if("toggle_active")
+ playsound(src, "terminal_type", 25, FALSE)
+ program.activated = !program.activated //we don't use the activation procs since we aren't in a mob
+ return TRUE
+ if("set_code")
+ var/new_code = text2num(params["code"])
+ playsound(src, "terminal_type", 25, FALSE)
+ var/target_code = params["target_code"]
+ switch(target_code)
+ if("activation")
+ program.activation_code = clamp(round(new_code, 1),0,9999)
+ if("deactivation")
+ program.deactivation_code = clamp(round(new_code, 1),0,9999)
+ if("kill")
+ program.kill_code = clamp(round(new_code, 1),0,9999)
+ if("trigger")
+ program.trigger_code = clamp(round(new_code, 1),0,9999)
+ return TRUE
+ if("set_extra_setting")
+ program.set_extra_setting(params["target_setting"], params["value"])
+ playsound(src, "terminal_type", 25, FALSE)
+ return TRUE
+ if("set_restart_timer")
+ var/timer = text2num(params["delay"])
+ if(!isnull(timer))
+ playsound(src, "terminal_type", 25, FALSE)
+ timer = clamp(round(timer, 1), 0, 3600)
+ timer *= 10 //convert to deciseconds
+ program.timer_restart = timer
+ return TRUE
+ if("set_shutdown_timer")
+ var/timer = text2num(params["delay"])
+ if(!isnull(timer))
+ playsound(src, "terminal_type", 25, FALSE)
+ timer = clamp(round(timer, 1), 0, 3600)
+ timer *= 10 //convert to deciseconds
+ program.timer_shutdown = timer
+ return TRUE
+ if("set_trigger_timer")
+ var/timer = text2num(params["delay"])
+ if(!isnull(timer))
+ playsound(src, "terminal_type", 25, FALSE)
+ timer = clamp(round(timer, 1), 0, 3600)
+ timer *= 10 //convert to deciseconds
+ program.timer_trigger = timer
+ return TRUE
+ if("set_timer_trigger_delay")
+ var/timer = text2num(params["delay"])
+ if(!isnull(timer))
+ playsound(src, "terminal_type", 25, FALSE)
+ timer = clamp(round(timer, 1), 0, 3600)
+ timer *= 10 //convert to deciseconds
+ program.timer_trigger_delay = timer
+ return TRUE
+
+/obj/machinery/nanite_programmer/proc/eject(mob/living/user)
+ if(!disk)
+ return
+ if(!istype(user) || !Adjacent(user) || !user.put_in_active_hand(disk))
+ disk.forceMove(drop_location())
+ disk = null
+ program = null
diff --git a/massmeta/features/nanites/code/mood_events.dm b/massmeta/features/nanites/code/mood_events.dm
new file mode 100644
index 0000000000000..3cd6db1174e48
--- /dev/null
+++ b/massmeta/features/nanites/code/mood_events.dm
@@ -0,0 +1,14 @@
+/datum/mood_event/nanite_sadness
+ description = "+++++++HAPPINESS SUPPRESSION+++++++"
+ mood_change = -7
+
+/datum/mood_event/nanite_sadness/add_effects(message)
+ description = "+++++++[message]+++++++"
+
+
+/datum/mood_event/nanite_happiness
+ description = "+++++++HAPPINESS ENHANCEMENT+++++++"
+ mood_change = 7
+
+/datum/mood_event/nanite_happiness/add_effects(message)
+ description = "+++++++[message]+++++++"
diff --git a/massmeta/features/nanites/code/nanite_backup.dm b/massmeta/features/nanites/code/nanite_backup.dm
new file mode 100644
index 0000000000000..92983bdd52d2f
--- /dev/null
+++ b/massmeta/features/nanites/code/nanite_backup.dm
@@ -0,0 +1,22 @@
+/datum/nanite_cloud_backup
+ ///The ID the backup is set to that is shown in the cloud host.
+ var/cloud_id = 0
+ ///The nanite component that the backup has, which handles listing programs and such.
+ var/datum/component/nanites/nanites
+ ///The host of our nanites in the cloud.
+ var/obj/machinery/computer/nanite_cloud_controller/cloud_host
+
+/datum/nanite_cloud_backup/New(obj/machinery/computer/nanite_cloud_controller/cloud_host_machine, cloud_host_id)
+ . = ..()
+ src.cloud_id = cloud_host_id
+ src.cloud_host = cloud_host_machine
+ cloud_host.cloud_backups += src
+ SSnanites.cloud_backups += src
+
+/datum/nanite_cloud_backup/proc/set_nanites(datum/component/nanites/cloud_copy)
+ src.nanites = cloud_copy
+
+/datum/nanite_cloud_backup/Destroy()
+ cloud_host.cloud_backups -= src
+ SSnanites.cloud_backups -= src
+ return ..()
diff --git a/massmeta/features/nanites/code/nanite_component.dm b/massmeta/features/nanites/code/nanite_component.dm
new file mode 100644
index 0000000000000..78b4cbf89d008
--- /dev/null
+++ b/massmeta/features/nanites/code/nanite_component.dm
@@ -0,0 +1,448 @@
+#define NANITE_DEFAULT_STARTING_VOLUME 100
+#define NANITE_DEFAULT_MAX_VOLUME 500
+#define NANITE_DEFAULT_REGEN_RATE 0.5
+#define NANITE_DEFAULT_SAFETY_THRESHOLD 50
+
+///The default amount of nanite research points to generate per person per tick, if unmodified.
+#define NANITE_BASE_RESEARCH 1.5
+///The chance at a Nanite program randomly failing when it cannot sync
+#define NANITE_FAILURE_CHANCE 8
+///The max amount of nanite programs you can have in a cloud at once.
+#define NANITE_PROGRAM_LIMIT 20
+///The delay between sync attempts for nanites to the cloud, if it fails then it will start to corrupt.
+#define NANITE_SYNC_DELAY (30 SECONDS)
+
+/datum/component/nanites
+ dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
+
+ ///The living person these nanites are attached onto
+ var/mob/living/host_mob
+
+ ///amount of nanites in the system, used as fuel for nanite programs
+ var/nanite_volume = NANITE_DEFAULT_STARTING_VOLUME
+ ///maximum amount of nanites someone can have
+ var/max_nanites = NANITE_DEFAULT_MAX_VOLUME
+ ///nanites generated per second
+ var/regen_rate = NANITE_DEFAULT_REGEN_RATE
+ ///how low nanites will get before they stop processing/triggering
+ var/safety_threshold = NANITE_DEFAULT_SAFETY_THRESHOLD
+ ///0 if not connected to the cloud, 1-100 to set a determined cloud backup to draw from
+ var/cloud_id = 0
+ ///How long until the next sync to cloud
+ var/next_sync = 0
+ ///All nanite programs in the user
+ var/list/datum/nanite_program/programs = list()
+
+ ///Separate list of protocol programs, to avoid looping through the whole programs list when checking for conflicts
+ var/list/datum/nanite_program/protocol/protocols = list()
+ ///Timestamp to when the nanites were first inserted in the host, used in some protocols.
+ var/start_time = 0
+ ///Prevents nanites from appearing on HUDs and health scans
+ var/stealth = FALSE
+ ///if TRUE, displays program list when scanned by nanite scanners
+ var/diagnostics = TRUE
+ ///The techweb these Nanites are synced to, to generate Nanite research points
+ var/datum/techweb/linked_techweb
+
+/datum/component/nanites/Initialize(
+ datum/techweb/linked_techweb,
+ nanite_volume = NANITE_DEFAULT_STARTING_VOLUME,
+ cloud_id = 0,
+)
+ if(!isliving(parent) && !istype(parent, /datum/nanite_cloud_backup))
+ return COMPONENT_INCOMPATIBLE
+
+ src.linked_techweb = linked_techweb
+ src.nanite_volume = nanite_volume
+ src.cloud_id = cloud_id
+
+ if(!isliving(parent))
+ return
+
+ host_mob = parent
+ if(!(host_mob.mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD))) //Shouldn't happen, but this avoids HUD runtimes in case a silicon gets them somehow.
+ return COMPONENT_INCOMPATIBLE
+
+ start_time = world.time
+
+ host_mob.hud_set_nanite_indicator()
+ START_PROCESSING(SSnanites, src)
+
+ if(cloud_id)
+ cloud_sync()
+
+/datum/component/nanites/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_NANITE_IS_STEALTHY, PROC_REF(check_stealth))
+ RegisterSignal(parent, COMSIG_NANITE_DELETE, PROC_REF(delete_nanites))
+ RegisterSignal(parent, COMSIG_NANITE_UI_DATA, PROC_REF(nanite_ui_data))
+ RegisterSignal(parent, COMSIG_NANITE_GET_PROGRAMS, PROC_REF(get_programs))
+ RegisterSignal(parent, COMSIG_NANITE_SET_VOLUME, PROC_REF(set_volume))
+ RegisterSignal(parent, COMSIG_NANITE_ADJUST_VOLUME, PROC_REF(adjust_nanites))
+ RegisterSignal(parent, COMSIG_NANITE_SET_MAX_VOLUME, PROC_REF(set_max_volume))
+ RegisterSignal(parent, COMSIG_NANITE_SET_CLOUD, PROC_REF(set_cloud))
+ RegisterSignal(parent, COMSIG_NANITE_SET_SAFETY, PROC_REF(set_safety))
+ RegisterSignal(parent, COMSIG_NANITE_SET_REGEN, PROC_REF(set_regen))
+ RegisterSignal(parent, COMSIG_NANITE_ADD_PROGRAM, PROC_REF(add_program))
+ RegisterSignal(parent, COMSIG_LIVING_HEALTHSCAN, PROC_REF(on_healthscan))
+ RegisterSignal(parent, COMSIG_NANITE_SCAN, PROC_REF(nanite_scan))
+ RegisterSignal(parent, COMSIG_NANITE_SYNC, PROC_REF(sync))
+ if(isliving(parent))
+ RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp))
+ RegisterSignal(parent, COMSIG_LIVING_DEATH, PROC_REF(on_death))
+ RegisterSignal(parent, COMSIG_MOB_TRIED_ACCESS, PROC_REF(on_tried_access))
+ RegisterSignal(parent, COMSIG_LIVING_ELECTROCUTE_ACT, PROC_REF(on_shock))
+ RegisterSignal(parent, COMSIG_LIVING_MINOR_SHOCK, PROC_REF(on_minor_shock))
+ RegisterSignal(parent, COMSIG_SPECIES_GAIN, PROC_REF(check_viable_biotype))
+ RegisterSignal(parent, COMSIG_NANITE_SIGNAL, PROC_REF(receive_signal))
+ RegisterSignal(parent, COMSIG_NANITE_COMM_SIGNAL, PROC_REF(receive_comm_signal))
+
+/datum/component/nanites/UnregisterFromParent()
+ UnregisterSignal(parent, list(
+ COMSIG_NANITE_IS_STEALTHY,
+ COMSIG_NANITE_DELETE,
+ COMSIG_NANITE_UI_DATA,
+ COMSIG_NANITE_GET_PROGRAMS,
+ COMSIG_NANITE_SET_VOLUME,
+ COMSIG_NANITE_ADJUST_VOLUME,
+ COMSIG_NANITE_SET_MAX_VOLUME,
+ COMSIG_NANITE_SET_CLOUD,
+ COMSIG_NANITE_SET_SAFETY,
+ COMSIG_NANITE_SET_REGEN,
+ COMSIG_NANITE_ADD_PROGRAM,
+ COMSIG_LIVING_HEALTHSCAN,
+ COMSIG_NANITE_SCAN,
+ COMSIG_NANITE_SYNC,
+ COMSIG_ATOM_EMP_ACT,
+ COMSIG_LIVING_DEATH,
+ COMSIG_MOB_TRIED_ACCESS,
+ COMSIG_LIVING_ELECTROCUTE_ACT,
+ COMSIG_LIVING_MINOR_SHOCK,
+ COMSIG_MOVABLE_HEAR,
+ COMSIG_SPECIES_GAIN,
+ COMSIG_NANITE_SIGNAL,
+ COMSIG_NANITE_COMM_SIGNAL,
+ ))
+
+/datum/component/nanites/Destroy()
+ STOP_PROCESSING(SSnanites, src)
+ QDEL_LIST(programs)
+ if(host_mob)
+ host_mob.hud_set_nanite_indicator(remove = TRUE)
+ set_nanite_bar(remove = TRUE)
+ host_mob = null
+ linked_techweb = null
+ return ..()
+
+/datum/component/nanites/InheritComponent(datum/component/nanites/new_nanites, i_am_original, amount, cloud)
+ if(new_nanites)
+ adjust_nanites(null, new_nanites.nanite_volume)
+ else
+ adjust_nanites(null, amount) //just add to the nanite volume
+
+/datum/component/nanites/process()
+ if(!HAS_TRAIT(host_mob, TRAIT_STASIS))
+ adjust_nanites(null, regen_rate)
+ add_research()
+ for(var/datum/nanite_program/active_programs as anything in programs)
+ active_programs.on_process()
+ if(cloud_id && world.time > next_sync)
+ cloud_sync()
+ next_sync = world.time + NANITE_SYNC_DELAY
+ set_nanite_bar()
+
+/datum/component/nanites/proc/delete_nanites()
+ SIGNAL_HANDLER
+
+ qdel(src)
+
+///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)
+ SIGNAL_HANDLER
+
+ var/list/programs_to_remove = programs.Copy()
+ var/list/programs_to_add = source.programs.Copy()
+ for(var/datum/nanite_program/nanite_program as anything in programs)
+ for(var/datum/nanite_program/adding_program as anything in programs_to_add)
+ if(nanite_program.type == adding_program.type)
+ programs_to_remove -= nanite_program
+ programs_to_add -= adding_program
+ adding_program.copy_programming(nanite_program, copy_activation)
+ break
+ if(full_overwrite)
+ for(var/X in programs_to_remove)
+ qdel(X)
+ for(var/datum/nanite_program/adding_program as anything in programs_to_add)
+ add_program(null, adding_program.copy())
+
+/datum/component/nanites/proc/cloud_sync()
+ if(!cloud_id)
+ return attempt_corrupt()
+ var/datum/nanite_cloud_backup/backup = SSnanites.get_cloud_backup(cloud_id)
+ if(!backup)
+ return attempt_corrupt()
+ var/datum/component/nanites/cloud_copy = backup.nanites
+ if(!cloud_copy)
+ return attempt_corrupt()
+ sync(null, cloud_copy)
+
+///Rolls for a chance to corrupt your nanites.
+/datum/component/nanites/proc/attempt_corrupt()
+ if(prob(NANITE_FAILURE_CHANCE) && programs.len)
+ var/datum/nanite_program/random_program = pick(programs)
+ random_program.software_error()
+
+/datum/component/nanites/proc/add_program(datum/source, datum/nanite_program/new_program, datum/nanite_program/source_program)
+ SIGNAL_HANDLER
+
+ if(istype(new_program, /datum/nanite_program/protocol))
+ var/datum/nanite_program/protocol/protocol_nanite = new_program
+ for(var/datum/nanite_program/protocol/other_protocols as anything in protocols)
+ //skip over the same type so it continues on to delete it later.
+ if(other_protocols.type != new_program.type)
+ continue
+ if(other_protocols.protocol_class != protocol_nanite.protocol_class)
+ continue
+ return COMPONENT_PROGRAM_NOT_INSTALLED
+
+ for(var/datum/nanite_program/all_program as anything in programs)
+ if(!all_program.unique || all_program.type != new_program.type)
+ continue
+ qdel(all_program)
+
+ if(programs.len >= NANITE_PROGRAM_LIMIT)
+ return COMPONENT_PROGRAM_NOT_INSTALLED
+ if(source_program)
+ source_program.copy_programming(new_program)
+ programs += new_program
+ new_program.on_add(src)
+ return COMPONENT_PROGRAM_INSTALLED
+
+/datum/component/nanites/proc/consume_nanites(amount, force = FALSE)
+ if(!force && safety_threshold && (nanite_volume - amount < safety_threshold))
+ return FALSE
+ adjust_nanites(null, -amount)
+ return (nanite_volume > 0)
+
+/datum/component/nanites/proc/adjust_nanites(datum/source, amount)
+ SIGNAL_HANDLER
+
+ nanite_volume = clamp(nanite_volume + amount, 0, max_nanites)
+ if(nanite_volume <= 0) //oops we ran out
+ INVOKE_ASYNC(src, PROC_REF(delete_nanites))
+
+/datum/component/nanites/proc/on_emp(datum/source, severity)
+ SIGNAL_HANDLER
+
+ 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))
+ cloud_id = 0
+ for(var/datum/nanite_program/all_program as anything in programs)
+ all_program.on_emp(severity)
+
+
+/datum/component/nanites/proc/on_shock(datum/source, shock_damage, siemens_coeff = 1, flags = NONE)
+ SIGNAL_HANDLER
+
+ if(flags & SHOCK_ILLUSION || shock_damage < 1)
+ return
+
+ if(!HAS_TRAIT_NOT_FROM(host_mob, TRAIT_SHOCKIMMUNE, TRAIT_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
+ 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)
+ SIGNAL_HANDLER
+
+ adjust_nanites(null, -(rand(5, 15))) //Lose 5-15 flat nanite volume
+ for(var/datum/nanite_program/all_program as anything in programs)
+ all_program.on_minor_shock()
+
+/datum/component/nanites/proc/check_stealth(datum/source)
+ SIGNAL_HANDLER
+
+ return stealth
+
+/datum/component/nanites/proc/on_death(datum/source, gibbed)
+ SIGNAL_HANDLER
+
+ for(var/datum/nanite_program/all_program as anything in programs)
+ all_program.on_death(gibbed)
+
+/datum/component/nanites/proc/receive_signal(datum/source, code, signal_source = "an unidentified source")
+ SIGNAL_HANDLER
+
+ for(var/datum/nanite_program/all_program as anything in programs)
+ all_program.receive_signal(code, signal_source)
+
+/datum/component/nanites/proc/receive_comm_signal(datum/source, comm_code, comm_message, comm_source = "an unidentified source")
+ SIGNAL_HANDLER
+
+ for(var/datum/nanite_program/comm/comm_program in programs)
+ comm_program.receive_comm_signal(comm_code, comm_message, comm_source)
+
+/datum/component/nanites/proc/check_viable_biotype()
+ SIGNAL_HANDLER
+
+ if(!(host_mob.mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD)))
+ qdel(src) //bodytype no longer sustains nanites
+
+/datum/component/nanites/proc/on_tried_access(datum/source, atom/locked_thing)
+ SIGNAL_HANDLER
+
+ if(!isobj(locked_thing))
+ return LOCKED_ATOM_INCOMPATIBLE
+
+ var/list/all_access = list()
+ var/obj/locked_object = locked_thing
+ for(var/datum/nanite_program/access/access_program in programs)
+ if(access_program.activated)
+ all_access += access_program.access
+
+ if(locked_object.check_access_list(all_access))
+ return ACCESS_ALLOWED
+
+ return ACCESS_DISALLOWED
+
+/datum/component/nanites/proc/set_volume(datum/source, amount)
+ SIGNAL_HANDLER
+
+ nanite_volume = clamp(amount, 0, max_nanites)
+
+/datum/component/nanites/proc/set_max_volume(datum/source, amount)
+ SIGNAL_HANDLER
+
+ max_nanites = max(1, max_nanites)
+
+/datum/component/nanites/proc/set_cloud(datum/source, amount)
+ SIGNAL_HANDLER
+
+ cloud_id = clamp(amount, 0, 100)
+
+/datum/component/nanites/proc/set_safety(datum/source, amount)
+ SIGNAL_HANDLER
+
+ safety_threshold = clamp(amount, 0, max_nanites)
+
+/datum/component/nanites/proc/set_regen(datum/source, amount)
+ SIGNAL_HANDLER
+
+ regen_rate = amount
+
+/datum/component/nanites/proc/get_data(list/nanite_data)
+ nanite_data["nanite_volume"] = nanite_volume
+ nanite_data["max_nanites"] = max_nanites
+ nanite_data["cloud_id"] = cloud_id
+ nanite_data["regen_rate"] = regen_rate
+ nanite_data["safety_threshold"] = safety_threshold
+ nanite_data["stealth"] = stealth
+
+/datum/component/nanites/proc/get_programs(datum/source, list/nanite_programs)
+ SIGNAL_HANDLER
+
+ nanite_programs |= programs
+
+///Adds nanite research points to the linked techweb based on the host's status.
+/datum/component/nanites/proc/add_research()
+ if(host_mob.stat == DEAD || !host_mob.client)
+ return
+ var/research_value = NANITE_BASE_RESEARCH
+ if(!ishuman(host_mob))
+ research_value *= 0.5
+ linked_techweb.add_point_list(list(TECHWEB_POINT_TYPE_NANITES = research_value))
+
+/datum/component/nanites/proc/on_healthscan(datum/source, list/render_list, advanced, mob/user, mode)
+ SIGNAL_HANDLER
+ nanite_scan(source, user, full_scan = FALSE)
+
+/datum/component/nanites/proc/nanite_scan(datum/source, mob/user, full_scan)
+ SIGNAL_HANDLER
+
+ if(full_scan)
+ to_chat(user, span_boldnotice("Nanites Detected"))
+ to_chat(user, span_info("Saturation: [nanite_volume]/[max_nanites]"))
+ to_chat(user, span_info("Safety Threshold: [safety_threshold]"))
+ to_chat(user, span_info("Cloud ID: [cloud_id ? cloud_id : "None"]"))
+ to_chat(user, span_info("================"))
+ to_chat(user, span_info("Program List:"))
+ if(diagnostics)
+ for(var/datum/nanite_program/NP as anything in programs)
+ to_chat(user, span_info("[NP.name] | [NP.activated ? "Active" : "Inactive"]"))
+ return TRUE
+ to_chat(user, span_alert("Diagnostics Disabled"))
+ return TRUE
+
+ if(stealth)
+ return TRUE
+
+ to_chat(user, span_boldnotice("Nanites Detected"))
+ to_chat(user, span_notice("Saturation: [nanite_volume]/[max_nanites]"))
+
+/datum/component/nanites/proc/nanite_ui_data(datum/source, list/data, scan_level)
+ SIGNAL_HANDLER
+
+ data["has_nanites"] = TRUE
+ data["nanite_volume"] = nanite_volume
+ data["regen_rate"] = regen_rate
+ data["safety_threshold"] = safety_threshold
+ data["cloud_id"] = cloud_id
+ var/list/mob_programs = list()
+ var/id = 1
+ for(var/datum/nanite_program/program as anything in programs)
+ var/list/mob_program = list()
+ mob_program["name"] = program.name
+ mob_program["desc"] = program.desc
+ mob_program["id"] = id
+
+ if(scan_level >= 2)
+ mob_program["activated"] = program.activated
+ mob_program["use_rate"] = program.use_rate
+ mob_program["can_trigger"] = program.can_trigger
+ mob_program["trigger_cost"] = program.trigger_cost
+ mob_program["trigger_cooldown"] = program.trigger_cooldown / 10
+
+ if(scan_level >= 3)
+ mob_program["timer_restart"] = program.timer_restart / 10
+ mob_program["timer_shutdown"] = program.timer_shutdown / 10
+ mob_program["timer_trigger"] = program.timer_trigger / 10
+ mob_program["timer_trigger_delay"] = program.timer_trigger_delay / 10
+ var/list/extra_settings = program.get_extra_settings_frontend()
+ mob_program["extra_settings"] = extra_settings
+ if(LAZYLEN(extra_settings))
+ mob_program["has_extra_settings"] = TRUE
+ else
+ mob_program["has_extra_settings"] = FALSE
+
+ if(scan_level >= 4)
+ mob_program["activation_code"] = program.activation_code
+ mob_program["deactivation_code"] = program.deactivation_code
+ mob_program["kill_code"] = program.kill_code
+ mob_program["trigger_code"] = program.trigger_code
+ var/list/rules = list()
+ var/rule_id = 1
+ for(var/datum/nanite_rule/nanite_rule as anything in program.rules)
+ var/list/rule = list()
+ rule["display"] = nanite_rule.display()
+ rule["program_id"] = id
+ rule["id"] = rule_id
+ rules += list(rule)
+ rule_id++
+ mob_program["rules"] = rules
+ if(LAZYLEN(rules))
+ mob_program["has_rules"] = TRUE
+ id++
+ mob_programs += list(mob_program)
+ data["mob_programs"] = mob_programs
+
+#undef NANITE_DEFAULT_STARTING_VOLUME
+#undef NANITE_DEFAULT_MAX_VOLUME
+#undef NANITE_DEFAULT_REGEN_RATE
+#undef NANITE_DEFAULT_SAFETY_THRESHOLD
+
+#undef NANITE_BASE_RESEARCH
+#undef NANITE_FAILURE_CHANCE
+#undef NANITE_PROGRAM_LIMIT
+#undef NANITE_SYNC_DELAY
diff --git a/massmeta/features/nanites/code/nanite_disease.dm b/massmeta/features/nanites/code/nanite_disease.dm
new file mode 100644
index 0000000000000..17f98cde968c2
--- /dev/null
+++ b/massmeta/features/nanites/code/nanite_disease.dm
@@ -0,0 +1,83 @@
+/*
+
+/datum/symptom/nanite_boost
+ name = "Nano-symbiosis"
+ desc = "The virus reacts to nanites in the host's bloodstream by enhancing their replication cycle."
+ stealth = 0
+ resistance = 2
+ stage_speed = 2
+ transmittable = -1
+ level = 7
+ severity = 0
+ symptom_delay_min = 1
+ symptom_delay_max = 1
+ threshold_descs = list(
+ "Transmission 5" = "Increases the virus' growth rate while nanites are present.",
+ "Stage Speed 7" = "Increases the replication boost.",
+ )
+ ///Whether nanites will also help boost the disease.
+ var/reverse_boost = FALSE
+
+/datum/symptom/nanite_boost/Start(datum/disease/advance/A)
+ . = ..()
+ if(!.)
+ return
+ if(A.properties["transmittable"] >= 5) //reverse boost
+ reverse_boost = TRUE
+ if(A.properties["stage_rate"] >= 7) //more nanites
+ power = 2
+
+/datum/symptom/nanite_boost/Activate(datum/disease/advance/A)
+ . = ..()
+ if(!.)
+ return
+ var/mob/living/carbon/M = A.affected_mob
+ SEND_SIGNAL(M, COMSIG_NANITE_ADJUST_VOLUME, 0.5 * power)
+ if(reverse_boost && M.GetComponent(/datum/component/nanites))
+ if(prob(A.stage_prob))
+ A.stage = min(A.stage + 1,A.max_stages)
+
+/datum/symptom/nanite_destroy
+ name = "Silicolysis"
+ desc = "The virus reacts to nanites in the host's bloodstream by attacking and consuming them."
+ stealth = 0
+ resistance = 4
+ stage_speed = -1
+ transmittable = 1
+ level = 7
+ severity = 0
+ symptom_delay_min = 1
+ symptom_delay_max = 1
+ threshold_descs = list(
+ "Stage Speed 5" = "Increases the virus' growth rate while nanites are present.",
+ "Resistance 7" = "Severely increases the rate at which the nanites are destroyed.",
+ )
+ ///Whether nanites will also help boost the disease.
+ var/reverse_boost = FALSE
+
+/datum/symptom/nanite_destroy/Start(datum/disease/advance/A)
+ . = ..()
+ if(!.)
+ return
+ if(A.properties["stage_rate"] >= 5) //reverse boost
+ reverse_boost = TRUE
+ if(A.properties["resistance"] >= 7) //less nanites
+ power = 3
+
+/datum/symptom/nanite_destroy/Activate(datum/disease/advance/A)
+ . = ..()
+ if(!.)
+ return
+ var/mob/living/carbon/M = A.affected_mob
+ SEND_SIGNAL(M, COMSIG_NANITE_ADJUST_VOLUME, -0.4 * power)
+ if(reverse_boost && M.GetComponent(/datum/component/nanites))
+ if(prob(A.stage_prob))
+ A.stage = min(A.stage + 1,A.max_stages)
+
+/datum/disease_ability/symptom/medium/nano_boost
+ symptoms = list(/datum/symptom/nanite_boost)
+
+/datum/disease_ability/symptom/medium/nano_destroy
+ symptoms = list(/datum/symptom/nanite_destroy)
+
+*/
diff --git a/massmeta/features/nanites/code/nanite_hud.dm b/massmeta/features/nanites/code/nanite_hud.dm
new file mode 100644
index 0000000000000..e12c1957196bd
--- /dev/null
+++ b/massmeta/features/nanites/code/nanite_hud.dm
@@ -0,0 +1,25 @@
+#define NANITE_HUD "nanite_hud"
+#define DIAG_NANITE_FULL_HUD "nanite_full_hud"
+
+/mob/living/proc/hud_set_nanite_indicator(remove = FALSE)
+ var/image/holder = hud_list[NANITE_HUD]
+ var/icon/I = icon(icon, icon_state, dir)
+ holder.pixel_y = I.Height() - world.icon_size
+ holder.icon = 'massmeta/features/nanites/icons/nanite_hud.dmi'
+ holder.icon_state = null
+ if(remove)
+ return //bye icon
+ holder.icon_state = "nanite_ping"
+
+///Updates the nanite volume bar visible in diagnostic HUDs
+/datum/component/nanites/proc/set_nanite_bar(remove = FALSE)
+ var/image/holder = host_mob.hud_list[DIAG_NANITE_FULL_HUD]
+ var/icon/I = icon(host_mob.icon, host_mob.icon_state, host_mob.dir)
+ holder.pixel_y = I.Height() - world.icon_size
+ holder.icon_state = null
+ if(remove || stealth)
+ return //bye icon
+ var/nanite_percent = (nanite_volume / max_nanites) * 100
+ nanite_percent = clamp(CEILING(nanite_percent, 10), 10, 100)
+ holder.icon = 'massmeta/features/nanites/icons/nanite_hud.dmi'
+ holder.icon_state = "nanites[nanite_percent]"
diff --git a/massmeta/features/nanites/code/nanite_remote.dm b/massmeta/features/nanites/code/nanite_remote.dm
new file mode 100644
index 0000000000000..93ec0281b81f7
--- /dev/null
+++ b/massmeta/features/nanites/code/nanite_remote.dm
@@ -0,0 +1,238 @@
+#define REMOTE_MODE_OFF "Off"
+#define REMOTE_MODE_SELF "Local"
+#define REMOTE_MODE_TARGET "Targeted"
+#define REMOTE_MODE_AOE "Area"
+#define REMOTE_MODE_RELAY "Relay"
+
+/obj/item/nanite_remote
+ name = "nanite remote control"
+ desc = "A device that can remotely control active nanites through wireless signals."
+ w_class = WEIGHT_CLASS_SMALL
+ req_access = list(ACCESS_RESEARCH)
+ icon = 'massmeta/features/nanites/icons/nanite_device.dmi'
+ icon_state = "nanite_remote"
+ base_icon_state = "nanite_remote"
+ item_flags = NOBLUDGEON
+
+ ///Boolean on whether the nanite remote has been locked, preventing changing of any setting.
+ var/locked = FALSE
+ var/mode = REMOTE_MODE_OFF
+ var/list/saved_settings = list()
+ var/last_id = 0
+ var/code = 0
+ var/relay_code = 0
+ var/current_program_name = "Program"
+
+/obj/item/nanite_remote/examine(mob/user)
+ . = ..()
+ if(locked)
+ . += span_notice("Alt-click to unlock.")
+
+/obj/item/nanite_remote/click_alt(mob/user)
+ if(!user.can_perform_action(src))
+ return
+ if(allowed(user))
+ locked = !locked
+ user.balloon_alert(user, (locked ? "locked" : "unlocked"))
+ update_appearance(UPDATE_ICON)
+ else
+ user.balloon_alert(user, "access denied!")
+
+/obj/item/nanite_remote/emag_act(mob/user)
+ if(obj_flags & EMAGGED)
+ return
+ to_chat(user, span_warning("You override [src]'s ID lock."))
+ obj_flags |= EMAGGED
+ if(locked)
+ locked = FALSE
+ update_appearance(UPDATE_ICON)
+
+/obj/item/nanite_remote/update_overlays()
+ . = ..()
+ if(obj_flags & EMAGGED)
+ . += "[base_icon_state]_emagged"
+ if(locked)
+ . += "[base_icon_state]_locked"
+
+/obj/item/nanite_remote/afterattack(atom/target, mob/user, proximity_flag, click_parameters, comms_message)
+ switch(mode)
+ if(REMOTE_MODE_OFF)
+ return
+ if(REMOTE_MODE_SELF)
+ to_chat(user, span_notice("You activate [src], signaling the nanites in your bloodstream."))
+ signal_mob(user, code, key_name(user))
+ return
+ if(REMOTE_MODE_TARGET)
+ if(isliving(target) && (get_dist(target, get_turf(src)) <= 7))
+ to_chat(user, span_notice("You activate [src], signaling the nanites inside [target]."))
+ signal_mob(target, code, key_name(user))
+ return
+ if(REMOTE_MODE_AOE)
+ to_chat(user, span_notice("You activate [src], signaling the nanites inside every host around you."))
+ for(var/mob/living/L in view(user, 7))
+ signal_mob(L, code, key_name(user))
+ return
+ if(REMOTE_MODE_RELAY)
+ to_chat(user, span_notice("You activate [src], signaling all connected relay nanites."))
+ signal_relay(code, relay_code, key_name(user))
+ return
+ return ..()
+
+/obj/item/nanite_remote/proc/signal_mob(mob/living/M, code, source)
+ SEND_SIGNAL(M, COMSIG_NANITE_SIGNAL, code, source)
+
+/obj/item/nanite_remote/proc/signal_relay(code, relay_code, source)
+ for(var/datum/nanite_program/relay/relays as anything in SSnanites.nanite_relays)
+ relays.relay_signal(code, relay_code, source)
+
+/obj/item/nanite_remote/ui_state(mob/user)
+ return GLOB.hands_state
+
+/obj/item/nanite_remote/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "NaniteRemote", name)
+ ui.open()
+
+/obj/item/nanite_remote/ui_data()
+ var/list/data = list()
+ data["code"] = code
+ data["relay_code"] = relay_code
+ data["mode"] = mode
+ data["locked"] = locked
+ data["saved_settings"] = saved_settings
+ data["program_name"] = current_program_name
+ return data
+
+/obj/item/nanite_remote/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("set_code")
+ if(locked)
+ return
+ var/new_code = text2num(params["code"])
+ if(!isnull(new_code))
+ new_code = clamp(round(new_code, 1),0,9999)
+ code = new_code
+ return TRUE
+ if("set_relay_code")
+ if(locked)
+ return
+ var/new_code = text2num(params["code"])
+ if(!isnull(new_code))
+ new_code = clamp(round(new_code, 1),0,9999)
+ relay_code = new_code
+ return TRUE
+ if("update_name")
+ current_program_name = params["name"]
+ return TRUE
+ if("save")
+ if(locked)
+ return
+ var/new_save = list()
+ new_save["id"] = last_id + 1
+ last_id++
+ new_save["name"] = current_program_name
+ new_save["code"] = code
+ new_save["mode"] = mode
+ new_save["relay_code"] = relay_code
+ saved_settings += list(new_save)
+ return TRUE
+ if("load")
+ var/code_id = params["save_id"]
+ var/list/setting
+ for(var/list/X in saved_settings)
+ if(X["id"] == text2num(code_id))
+ setting = X
+ break
+ if(setting)
+ code = setting["code"]
+ mode = setting["mode"]
+ relay_code = setting["relay_code"]
+ return TRUE
+ if("remove_save")
+ if(locked)
+ return
+ var/code_id = params["save_id"]
+ for(var/list/setting in saved_settings)
+ if(setting["id"] == text2num(code_id))
+ saved_settings -= list(setting)
+ break
+ return TRUE
+ if("select_mode")
+ if(locked)
+ return
+ mode = params["mode"]
+ return TRUE
+ if("lock")
+ if(!(obj_flags & EMAGGED))
+ locked = TRUE
+ update_appearance()
+ return TRUE
+
+
+/obj/item/nanite_remote/comm
+ name = "nanite communication remote"
+ desc = "A device that can send text messages to specific programs."
+ icon_state = "nanite_comm_remote"
+ var/comm_message = ""
+
+/obj/item/nanite_remote/comm/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+ switch(mode)
+ if(REMOTE_MODE_OFF)
+ return
+ if(REMOTE_MODE_SELF)
+ to_chat(user, span_notice("You activate [src], signaling the nanites in your bloodstream."))
+ signal_mob(user, code, comm_message)
+ if(REMOTE_MODE_TARGET)
+ if(isliving(target) && (get_dist(target, get_turf(src)) <= 7))
+ to_chat(user, span_notice("You activate [src], signaling the nanites inside [target]."))
+ signal_mob(target, code, comm_message, key_name(user))
+ if(REMOTE_MODE_AOE)
+ to_chat(user, span_notice("You activate [src], signaling the nanites inside every host around you."))
+ for(var/mob/living/L in view(user, 7))
+ signal_mob(L, code, comm_message, key_name(user))
+ if(REMOTE_MODE_RELAY)
+ to_chat(user, span_notice("You activate [src], signaling all connected relay nanites."))
+ signal_relay(code, relay_code, comm_message, key_name(user))
+
+/obj/item/nanite_remote/comm/signal_mob(mob/living/M, code, source)
+ SEND_SIGNAL(M, COMSIG_NANITE_COMM_SIGNAL, code, comm_message)
+
+/obj/item/nanite_remote/comm/signal_relay(code, relay_code, source)
+ for(var/datum/nanite_program/relay/relays as anything in SSnanites.nanite_relays)
+ relays.relay_comm_signal(code, relay_code, comm_message)
+
+/obj/item/nanite_remote/comm/ui_data()
+ var/list/data = list()
+ data["comms"] = TRUE
+ data["code"] = code
+ data["relay_code"] = relay_code
+ data["message"] = comm_message
+ data["mode"] = mode
+ data["locked"] = locked
+ data["saved_settings"] = saved_settings
+ data["program_name"] = current_program_name
+ return data
+
+/obj/item/nanite_remote/comm/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("set_message")
+ if(locked)
+ return
+ var/new_message = html_encode(params["value"])
+ if(!new_message)
+ return
+ comm_message = new_message
+ return TRUE
+
+#undef REMOTE_MODE_OFF
+#undef REMOTE_MODE_SELF
+#undef REMOTE_MODE_TARGET
+#undef REMOTE_MODE_AOE
+#undef REMOTE_MODE_RELAY
diff --git a/massmeta/features/nanites/code/nanite_scanner.dm b/massmeta/features/nanites/code/nanite_scanner.dm
new file mode 100644
index 0000000000000..b9c06e8face88
--- /dev/null
+++ b/massmeta/features/nanites/code/nanite_scanner.dm
@@ -0,0 +1,25 @@
+/obj/item/nanite_scanner
+ name = "nanite scanner"
+ icon = 'massmeta/features/nanites/icons/nanite_device.dmi'
+ icon_state = "nanite_scanner"
+ worn_icon_state = "electronic"
+ lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
+ desc = "A hand-held body scanner able to detect nanites and their programming."
+ obj_flags = CONDUCTS_ELECTRICITY
+ item_flags = NOBLUDGEON
+ slot_flags = ITEM_SLOT_BELT
+ throwforce = 3
+ w_class = WEIGHT_CLASS_TINY
+ throw_speed = 3
+ throw_range = 7
+ custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT * 2)
+
+/obj/item/nanite_scanner/attack(mob/living/target_mob, mob/living/user, params)
+ add_fingerprint(user)
+ user.visible_message(span_notice("[user] analyzes [target_mob]'s nanites."))
+ balloon_alert(user, "analyzing nanites")
+ playsound(user.loc, 'massmeta/features/nanites/sound/nanite_scan.mp3', 50)
+ var/response = SEND_SIGNAL(target_mob, COMSIG_NANITE_SCAN, user, TRUE)
+ if(!response)
+ to_chat(user, span_info("No nanites detected in the subject."))
diff --git a/massmeta/features/nanites/code/nanite_subsystem.dm b/massmeta/features/nanites/code/nanite_subsystem.dm
new file mode 100644
index 0000000000000..68baf90729777
--- /dev/null
+++ b/massmeta/features/nanites/code/nanite_subsystem.dm
@@ -0,0 +1,22 @@
+PROCESSING_SUBSYSTEM_DEF(nanites)
+ name = "Nanites"
+ flags = SS_BACKGROUND|SS_POST_FIRE_TIMING|SS_NO_INIT
+ wait = 1 SECONDS
+
+ ///List of all Nanite backups in the game.
+ var/list/datum/nanite_cloud_backup/cloud_backups = list()
+ ///List of all nanite relays in the game.
+ var/list/datum/nanite_program/relay/nanite_relays = list()
+
+/datum/controller/subsystem/processing/nanites/proc/check_hardware(datum/nanite_cloud_backup/backup)
+ if(QDELETED(backup.cloud_host) || (backup.cloud_host.machine_stat & (NOPOWER|BROKEN)))
+ return FALSE
+ return TRUE
+
+///Using a given cloud_id, will try to sync to the proper nanite cloud backup.
+/datum/controller/subsystem/processing/nanites/proc/get_cloud_backup(cloud_id, force = FALSE)
+ for(var/datum/nanite_cloud_backup/backup as anything in cloud_backups)
+ if(!force && !check_hardware(backup))
+ return
+ if(backup.cloud_id == cloud_id)
+ return backup
diff --git a/massmeta/features/nanites/code/nanites_decal.dm b/massmeta/features/nanites/code/nanites_decal.dm
new file mode 100644
index 0000000000000..94508afc9bf97
--- /dev/null
+++ b/massmeta/features/nanites/code/nanites_decal.dm
@@ -0,0 +1,8 @@
+/obj/structure/sign/departments/nanites
+ name = "\improper Nanite Lab sign"
+ sign_change_name = "Department - Science: Nanites"
+ desc = "A sign labelling an area where testing and development of nanites is performed."
+ icon = 'massmeta/features/nanites/icons/nanites_decal.dmi'
+ icon_state = "nanites"
+
+MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/nanites, 32)
diff --git a/massmeta/features/nanites/code/programs/_programs.dm b/massmeta/features/nanites/code/programs/_programs.dm
new file mode 100644
index 0000000000000..92ea0dcc99760
--- /dev/null
+++ b/massmeta/features/nanites/code/programs/_programs.dm
@@ -0,0 +1,331 @@
+/datum/nanite_program
+ ///The name of the Nanite Program.
+ var/name = "Generic Nanite Program"
+ ///The description of the nanite program for easy understanding.
+ var/desc = "Warn a coder if you can read this."
+
+ ///The nanite program component we're connected to.
+ var/datum/component/nanites/nanites
+ ///The person hosting this nanite program, if any.
+ var/mob/living/host_mob
+
+ ///How many nanites this uses while active.
+ var/use_rate = 0
+ ///Boolean on whether there can have more than one of this nanite program in the same nanite component.
+ var/unique = TRUE
+ ///Boolean on whether the nanite program has a trigger function.
+ var/can_trigger = FALSE
+ ///Amount of nanites this uses when triggered.
+ var/trigger_cost = 0
+ ///Dediseconds required between each trigger activation.
+ var/trigger_cooldown = 50
+ ///World time required for the next trigger activation.
+ var/next_trigger = 0
+
+ ///Special program flags.
+ ///(NANITE_SHOCK_IMMUNE | NANITE_EMP_IMMUNE)
+ var/program_flags = NONE
+ ///Boolean on whether the nanites have an on/off style effect.
+ var/passive_enabled = FALSE
+
+ ///What this program turns into if it glitches. This should be simpler, negative, waste of nanites, or affecting the same bodyparts. Generally.
+ var/list/rogue_types = list(/datum/nanite_program/glitch)
+
+ ///Boolean whether the program is currently activated. Off programs won't proceess or have passive effects, but won't consume nanites.
+ var/activated = TRUE
+
+ ///How many deciseconds to wait upon deactivation before self-reactivating. Also works if the program begins deactivated.
+ var/timer_restart = 0
+ ///How many deciseconds to wait upon activation before self-deactivating. Also works if the program begins activated.
+ var/timer_shutdown = 0
+ ///While active, how many dediseconds before the program attempts to trigger itself.
+ var/timer_trigger = 0
+ ///While active, how many dediseconds the program will deelay its trigger signals.
+ var/timer_trigger_delay = 0
+
+ //Indicates the next world.time tick where these timers will act
+ var/timer_restart_next = 0
+ var/timer_shutdown_next = 0
+ var/timer_trigger_next = 0
+ var/timer_trigger_delay_next = 0
+
+ ///Code that activates the program when sent. [1-9999, 0 ignores signals]
+ var/activation_code = 0
+ ///Code that deactivates the program [1-9999, 0 ignores signals]
+ var/deactivation_code = 0
+ ///Code that permanently removes the program [1-9999, 0 ignores signals]
+ var/kill_code = 0
+ ///Code that triggers the program (if available) [1-9999, 0 ignores signals]
+ var/trigger_code = 0
+
+ ///Boolean on whether all rules are required for positive condition or any of specified.
+ var/all_rules_required = TRUE
+ ///Rules that automatically manage if the program's active without requiring separate sensor programs
+ var/list/datum/nanite_rule/rules = list()
+
+ ///List of extra settings the nanite program is following.
+ VAR_FINAL/list/datum/nanite_extra_setting/extra_settings = list()
+
+/datum/nanite_program/New()
+ . = ..()
+ register_extra_settings()
+
+/datum/nanite_program/Destroy()
+ extra_settings = null
+ if(host_mob)
+ if(activated)
+ deactivate()
+ if(passive_enabled)
+ disable_passive_effect()
+ on_mob_remove()
+ if(nanites)
+ nanites.programs -= src
+ nanites = null
+ return ..()
+
+/datum/nanite_program/proc/copy()
+ var/datum/nanite_program/new_program = new type()
+ copy_programming(new_program, TRUE)
+ return new_program
+
+/datum/nanite_program/proc/copy_programming(datum/nanite_program/target, copy_activated = TRUE)
+ if(copy_activated)
+ target.activated = activated
+ target.timer_restart = timer_restart
+ target.timer_shutdown = timer_shutdown
+ target.timer_trigger = timer_trigger
+ target.timer_trigger_delay = timer_trigger_delay
+ target.activation_code = activation_code
+ target.deactivation_code = deactivation_code
+ target.kill_code = kill_code
+ target.trigger_code = trigger_code
+
+ target.rules = list()
+ for(var/datum/nanite_rule/rule as anything in rules)
+ rule.copy_to(target)
+ target.all_rules_required = all_rules_required
+
+ if(istype(target,src))
+ copy_extra_settings_to(target)
+
+///Register extra settings by overriding this.
+///extra_settings[name] = new typepath() for each extra setting
+/datum/nanite_program/proc/register_extra_settings()
+ return
+
+///You can override this if you need to have special behavior after setting certain settings.
+/datum/nanite_program/proc/set_extra_setting(setting, value)
+ var/datum/nanite_extra_setting/ES = extra_settings[setting]
+ return ES.set_value(value)
+
+///You probably shouldn't be overriding this one, but I'm not a cop.
+/datum/nanite_program/proc/get_extra_setting_value(setting)
+ var/datum/nanite_extra_setting/ES = extra_settings[setting]
+ return ES.get_value()
+
+///Used for getting information about the extra settings to the frontend
+/datum/nanite_program/proc/get_extra_settings_frontend()
+ var/list/out = list()
+ for(var/name in extra_settings)
+ var/datum/nanite_extra_setting/ES = extra_settings[name]
+ out += ES.get_frontend_list(name)
+ return out
+
+///Copy of the list instead of direct reference for obvious reasons
+/datum/nanite_program/proc/copy_extra_settings_to(datum/nanite_program/target)
+ var/list/copy_list = list()
+ for(var/ns_name in extra_settings)
+ var/datum/nanite_extra_setting/extra_setting = extra_settings[ns_name]
+ copy_list[ns_name] = extra_setting.get_copy()
+ target.extra_settings = copy_list
+
+/datum/nanite_program/proc/on_add(datum/component/nanites/_nanites)
+ nanites = _nanites
+ if(nanites.host_mob)
+ on_mob_add()
+
+/datum/nanite_program/proc/on_mob_add()
+ SHOULD_CALL_PARENT(TRUE)
+ host_mob = nanites.host_mob
+ if(activated) //apply activation effects depending on initial status; starts the restart and shutdown timers
+ activate()
+ else
+ deactivate()
+
+/datum/nanite_program/proc/on_mob_remove()
+ SHOULD_CALL_PARENT(TRUE)
+ return
+
+/datum/nanite_program/proc/toggle()
+ if(!activated)
+ activate()
+ else
+ deactivate()
+
+/datum/nanite_program/proc/activate()
+ activated = TRUE
+ if(timer_shutdown)
+ timer_shutdown_next = world.time + timer_shutdown
+
+/datum/nanite_program/proc/deactivate()
+ if(passive_enabled)
+ disable_passive_effect()
+ activated = FALSE
+ if(timer_restart)
+ timer_restart_next = world.time + timer_restart
+
+/datum/nanite_program/proc/on_process()
+ if(!activated)
+ if(timer_restart_next && world.time > timer_restart_next)
+ activate()
+ timer_restart_next = 0
+ return
+
+ if(timer_shutdown_next && world.time > timer_shutdown_next)
+ deactivate()
+ timer_shutdown_next = 0
+ return
+
+ if(timer_trigger && world.time > timer_trigger_next)
+ trigger()
+ timer_trigger_next = world.time + timer_trigger
+ return
+
+ if(timer_trigger_delay_next && world.time > timer_trigger_delay_next)
+ trigger(delayed = TRUE)
+ timer_trigger_delay_next = 0
+ return
+
+ if(check_conditions() && consume_nanites(use_rate))
+ if(!passive_enabled)
+ enable_passive_effect()
+ active_effect()
+ return
+
+ if(passive_enabled)
+ disable_passive_effect()
+
+//If false, disables active and passive effects, but doesn't consume nanites
+//Can be used to avoid consuming nanites for nothing
+/datum/nanite_program/proc/check_conditions()
+ if(!LAZYLEN(rules))
+ return TRUE
+ for(var/datum/nanite_rule/rule as anything in rules)
+ if(!all_rules_required && rule.check_rule())
+ return TRUE
+ if(all_rules_required && !rule.check_rule())
+ return FALSE
+ return all_rules_required
+
+//Constantly procs as long as the program is active
+/datum/nanite_program/proc/active_effect()
+ return
+
+//Procs once when the program activates
+/datum/nanite_program/proc/enable_passive_effect()
+ passive_enabled = TRUE
+
+//Procs once when the program deactivates
+/datum/nanite_program/proc/disable_passive_effect()
+ passive_enabled = FALSE
+
+//Checks conditions then fires the nanite trigger effect
+/datum/nanite_program/proc/trigger(delayed = FALSE, comm_message)
+ if(!can_trigger)
+ return
+ if(!activated)
+ return
+ if(timer_trigger_delay && !delayed)
+ timer_trigger_delay_next = world.time + timer_trigger_delay
+ return
+ if(world.time < next_trigger)
+ return
+ if(!consume_nanites(trigger_cost))
+ return
+ next_trigger = world.time + trigger_cooldown
+ on_trigger(comm_message)
+
+//Nanite trigger effect, requires can_trigger to be used
+/datum/nanite_program/proc/on_trigger(comm_message)
+ return
+
+/datum/nanite_program/proc/consume_nanites(amount, force = FALSE)
+ return nanites.consume_nanites(amount, force)
+
+/datum/nanite_program/proc/on_emp(severity)
+ if(program_flags & NANITE_EMP_IMMUNE)
+ return
+ if(prob(80 / severity))
+ software_error()
+
+/datum/nanite_program/proc/on_shock(shock_damage)
+ if(!(program_flags & NANITE_SHOCK_IMMUNE))
+ if(prob(10))
+ software_error()
+ else if(prob(33))
+ qdel(src)
+
+/datum/nanite_program/proc/on_minor_shock()
+ if(!(program_flags & NANITE_SHOCK_IMMUNE))
+ if(prob(10))
+ software_error()
+
+/datum/nanite_program/proc/on_death(gibbed)
+ return
+
+#define SOFTWARE_ERROR_DELETE 1
+#define SOFTWARE_ERROR_DEPROGRAM 2
+#define SOFTWARE_ERROR_TOGGLE 3
+#define SOFTWARE_ERROR_TRIGGER 4
+#define SOFTWARE_ERROR_ROGUE 5
+
+/datum/nanite_program/proc/software_error()
+ var/list/software_errors_weighted = list(
+ SOFTWARE_ERROR_DELETE = 1,
+ SOFTWARE_ERROR_DEPROGRAM = 2,
+ SOFTWARE_ERROR_TOGGLE = 2,
+ SOFTWARE_ERROR_TRIGGER = 2,
+ SOFTWARE_ERROR_ROGUE = 3,
+ )
+ var/error_type = pick_weight(software_errors_weighted)
+ switch(error_type)
+ if(SOFTWARE_ERROR_DELETE)
+ qdel(src) //kill switch
+ if(SOFTWARE_ERROR_DEPROGRAM) //deprogram codes
+ activation_code = 0
+ deactivation_code = 0
+ kill_code = 0
+ trigger_code = 0
+ if(SOFTWARE_ERROR_TOGGLE)
+ toggle() //enable/disable
+ if(SOFTWARE_ERROR_TRIGGER)
+ if(can_trigger)
+ trigger()
+ else
+ toggle() //enable/disable
+ if(SOFTWARE_ERROR_ROGUE) //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)
+
+#undef SOFTWARE_ERROR_DELETE
+#undef SOFTWARE_ERROR_DEPROGRAM
+#undef SOFTWARE_ERROR_TOGGLE
+#undef SOFTWARE_ERROR_TRIGGER
+#undef SOFTWARE_ERROR_ROGUE
+
+/datum/nanite_program/proc/receive_signal(code, source)
+ if(activation_code && code == activation_code && !activated)
+ activate()
+ log_game("[host_mob]'s [name] nanite program was activated by [source] with code [code].")
+ else if(deactivation_code && code == deactivation_code && activated)
+ deactivate()
+ log_game("[host_mob]'s [name] nanite program was deactivated by [source] with code [code].")
+ if(can_trigger && trigger_code && code == trigger_code)
+ trigger()
+ log_game("[host_mob]'s [name] nanite program was triggered by [source] with code [code].")
+ if(kill_code && code == kill_code)
+ log_game("[host_mob]'s [name] nanite program was deleted by [source] with code [code].")
+ qdel(src)
+
diff --git a/massmeta/features/nanites/code/programs/buffing.dm b/massmeta/features/nanites/code/programs/buffing.dm
new file mode 100644
index 0000000000000..1e1e6e8a135a8
--- /dev/null
+++ b/massmeta/features/nanites/code/programs/buffing.dm
@@ -0,0 +1,128 @@
+/datum/nanite_program/nervous
+ name = "Nerve Support"
+ desc = "The nanites act as a secondary nervous system, reducing the amount of time the host is stunned."
+ use_rate = 1.5
+ rogue_types = list(/datum/nanite_program/nerve_decay)
+
+/datum/nanite_program/nervous/enable_passive_effect()
+ . = ..()
+ if(ishuman(host_mob))
+ var/mob/living/carbon/human/host_human = host_mob
+ host_human.physiology.stun_mod *= 0.5
+
+/datum/nanite_program/nervous/disable_passive_effect()
+ . = ..()
+ if(ishuman(host_mob))
+ var/mob/living/carbon/human/host_human = host_mob
+ host_human.physiology.stun_mod *= 2
+
+/datum/nanite_program/adrenaline
+ name = "Adrenaline Burst"
+ desc = "The nanites cause a burst of adrenaline when triggered, waking the host from stuns and temporarily increasing their speed."
+ can_trigger = TRUE
+ trigger_cost = 25
+ trigger_cooldown = 1200
+ rogue_types = list(/datum/nanite_program/toxic, /datum/nanite_program/nerve_decay)
+
+/datum/nanite_program/adrenaline/on_trigger()
+ to_chat(host_mob, span_notice("You feel a sudden surge of energy!"))
+ host_mob.SetAllImmobility(0)
+ host_mob.adjustStaminaLoss(-75)
+ host_mob.set_resting(FALSE)
+ host_mob.reagents.add_reagent(/datum/reagent/medicine/stimulants, 1.5)
+
+/datum/nanite_program/hardening
+ name = "Dermal Hardening"
+ desc = "The nanites form a mesh under the host's skin, protecting them from melee and bullet impacts."
+ use_rate = 1.0
+ rogue_types = list(/datum/nanite_program/skin_decay)
+
+/datum/armor/nanite_hardening
+ melee = 25
+ bullet = 20
+
+//TODO on_hit effect that turns skin grey for a moment
+
+/datum/nanite_program/hardening/enable_passive_effect()
+ . = ..()
+ if(ishuman(host_mob))
+ var/mob/living/carbon/human/user = host_mob
+ user.physiology.armor = user.physiology.armor.add_other_armor(/datum/armor/nanite_hardening)
+
+/datum/nanite_program/hardening/disable_passive_effect()
+ . = ..()
+ if(ishuman(host_mob))
+ var/mob/living/carbon/human/user = host_mob
+ user.physiology.armor = user.physiology.armor.subtract_other_armor(/datum/armor/nanite_hardening)
+
+/datum/nanite_program/refractive
+ name = "Dermal Refractive Surface"
+ desc = "The nanites form a membrane above the host's skin, reducing the effect of laser and energy impacts."
+ use_rate = 1.0
+ rogue_types = list(/datum/nanite_program/skin_decay)
+
+/datum/armor/nanite_refractive
+ laser = 25
+ energy = 20
+
+/datum/nanite_program/refractive/enable_passive_effect()
+ . = ..()
+ if(ishuman(host_mob))
+ var/mob/living/carbon/human/user = host_mob
+ user.physiology.armor = user.physiology.armor.add_other_armor(/datum/armor/nanite_refractive)
+
+/datum/nanite_program/refractive/disable_passive_effect()
+ . = ..()
+ if(ishuman(host_mob))
+ var/mob/living/carbon/human/user = host_mob
+ user.physiology.armor = user.physiology.armor.subtract_other_armor(/datum/armor/nanite_refractive)
+
+/datum/nanite_program/coagulating
+ name = "Rapid Coagulation"
+ desc = "The nanites induce rapid coagulation when the host is wounded, dramatically reducing bleeding rate."
+ use_rate = 0.10
+ rogue_types = list(/datum/nanite_program/suffocating)
+
+/datum/nanite_program/coagulating/enable_passive_effect()
+ . = ..()
+ if(ishuman(host_mob))
+ var/mob/living/carbon/human/host_human = host_mob
+ host_human.physiology.bleed_mod *= 0.1
+
+/datum/nanite_program/coagulating/disable_passive_effect()
+ . = ..()
+ if(ishuman(host_mob))
+ var/mob/living/carbon/human/host_human = host_mob
+ host_human.physiology.bleed_mod *= 10
+
+/datum/nanite_program/conductive
+ name = "Electric Conduction"
+ desc = "The nanites act as a grounding rod for electric shocks, protecting the host. Shocks can still damage the nanites themselves."
+ use_rate = 0.20
+ program_flags = NANITE_SHOCK_IMMUNE
+ rogue_types = list(/datum/nanite_program/nerve_decay)
+
+/datum/nanite_program/conductive/enable_passive_effect()
+ . = ..()
+ ADD_TRAIT(host_mob, TRAIT_SHOCKIMMUNE, TRAIT_NANITES)
+
+/datum/nanite_program/conductive/disable_passive_effect()
+ . = ..()
+ REMOVE_TRAIT(host_mob, TRAIT_SHOCKIMMUNE, TRAIT_NANITES)
+
+/datum/nanite_program/mindshield
+ name = "Mental Barrier"
+ desc = "The nanites form a protective membrane around the host's brain, shielding them from abnormal influences while they're active."
+ use_rate = 0.40
+ rogue_types = list(/datum/nanite_program/brain_decay, /datum/nanite_program/brain_misfire)
+
+/datum/nanite_program/mindshield/enable_passive_effect()
+ . = ..()
+ if(!host_mob.mind.has_antag_datum(/datum/antagonist/rev, TRUE)) //won't work if on a rev, to avoid having implanted revs.
+ ADD_TRAIT(host_mob, TRAIT_MINDSHIELD, TRAIT_NANITES)
+ host_mob.sec_hud_set_implants()
+
+/datum/nanite_program/mindshield/disable_passive_effect()
+ . = ..()
+ REMOVE_TRAIT(host_mob, TRAIT_MINDSHIELD, TRAIT_NANITES)
+ host_mob.sec_hud_set_implants()
diff --git a/massmeta/features/nanites/code/programs/healing.dm b/massmeta/features/nanites/code/programs/healing.dm
new file mode 100644
index 0000000000000..42cca6c0f953b
--- /dev/null
+++ b/massmeta/features/nanites/code/programs/healing.dm
@@ -0,0 +1,230 @@
+/datum/nanite_program/regenerative
+ name = "Accelerated Regeneration"
+ desc = "The nanites boost the host's natural regeneration, increasing their healing speed. \
+ Does not consume nanites if the host is unharmed. \
+ Works better in low-pressure environments."
+ use_rate = 0.5
+ rogue_types = list(/datum/nanite_program/necrotic)
+
+/datum/nanite_program/regenerative/check_conditions()
+ if(!host_mob.getBruteLoss() && !host_mob.getFireLoss())
+ return FALSE
+ if(iscarbon(host_mob))
+ var/mob/living/carbon/host_carbon = host_mob
+ var/list/parts = host_carbon.get_damaged_bodyparts(brute = TRUE, burn = TRUE, required_bodytype = BODYTYPE_ORGANIC)
+ if(!parts.len)
+ return FALSE
+ return ..()
+
+/datum/nanite_program/regenerative/active_effect()
+ if(!iscarbon(host_mob))
+ host_mob.adjustBruteLoss(-0.5, TRUE)
+ host_mob.adjustFireLoss(-0.5, TRUE)
+ return
+ var/lavaland_bonus = (lavaland_equipment_pressure_check(get_turf(host_mob)) ? 1 : 0.6) // 0.5 on lavaland, 0.3 on station
+ host_mob.heal_overall_damage(brute = (0.5 * lavaland_bonus), brute = (0.5 * lavaland_bonus), required_bodytype = BODYTYPE_ORGANIC)
+
+/datum/nanite_program/regenerative_advanced
+ name = "Bio-Reconstruction"
+ desc = "The nanites manually repair and replace organic cells, acting much faster than normal regeneration. \
+ However, this program cannot detect the difference between harmed and unharmed, causing it to consume nanites even if it has no effect. \
+ Works better in low-pressure environments."
+ use_rate = 5.5
+ rogue_types = list(/datum/nanite_program/suffocating, /datum/nanite_program/necrotic)
+
+/datum/nanite_program/regenerative_advanced/active_effect()
+ if(!iscarbon(host_mob))
+ host_mob.adjustBruteLoss(-3, TRUE)
+ host_mob.adjustFireLoss(-3, TRUE)
+ return
+ var/lavaland_bonus = (lavaland_equipment_pressure_check(get_turf(host_mob)) ? 1 : 0.8) // 1.5 on Lavaland, 1.2 on station
+ host_mob.heal_overall_damage(brute = (1.5 * lavaland_bonus), brute = (1.5 * lavaland_bonus), required_bodytype = BODYTYPE_ROBOTIC)
+
+/datum/nanite_program/temperature
+ name = "Temperature Adjustment"
+ desc = "The nanites adjust the host's internal temperature to an ideal level. Does not consume nanites if the host has a nominal temperature."
+ use_rate = 3.5
+ rogue_types = list(/datum/nanite_program/skin_decay)
+
+/datum/nanite_program/temperature/check_conditions()
+ if(host_mob.bodytemperature > (host_mob.get_body_temp_normal(apply_change = FALSE) - 30) && host_mob.bodytemperature < (host_mob.get_body_temp_normal(apply_change = FALSE) + 30))
+ return FALSE
+ return ..()
+
+/datum/nanite_program/temperature/active_effect()
+ if(host_mob.bodytemperature > host_mob.get_body_temp_normal(apply_change=FALSE))
+ host_mob.adjust_bodytemperature(-40 * TEMPERATURE_DAMAGE_COEFFICIENT, host_mob.get_body_temp_normal(apply_change=FALSE))
+ else if(host_mob.bodytemperature < (host_mob.get_body_temp_normal(apply_change=FALSE) + 1))
+ host_mob.adjust_bodytemperature(40 * TEMPERATURE_DAMAGE_COEFFICIENT, 0, host_mob.get_body_temp_normal(apply_change=FALSE))
+
+/datum/nanite_program/purging
+ name = "Blood Purification"
+ desc = "The nanites purge toxins and chemicals from the host's bloodstream."
+ use_rate = 1
+ rogue_types = list(/datum/nanite_program/suffocating, /datum/nanite_program/necrotic)
+
+/datum/nanite_program/purging/check_conditions()
+ var/foreign_reagent = length(host_mob.reagents?.reagent_list)
+ if(!host_mob.getToxLoss() && !foreign_reagent)
+ return FALSE
+ return ..()
+
+/datum/nanite_program/purging/active_effect()
+ host_mob.adjustToxLoss(-1)
+ for(var/datum/reagent/reagents as anything in host_mob.reagents.reagent_list)
+ host_mob.reagents.remove_reagent(reagents.type, amount = 1)
+
+/datum/nanite_program/brain_heal
+ name = "Neural Regeneration"
+ desc = "The nanites fix neural connections in the host's brain, reversing brain damage and minor traumas."
+ use_rate = 1.5
+ rogue_types = list(/datum/nanite_program/brain_decay)
+
+/datum/nanite_program/brain_heal/check_conditions()
+ var/problems = FALSE
+ if(iscarbon(host_mob))
+ var/mob/living/carbon/carbon_host = host_mob
+ if(length(carbon_host.get_traumas()))
+ problems = TRUE
+ if(host_mob.get_organ_loss(ORGAN_SLOT_BRAIN) > 0)
+ problems = TRUE
+ return problems ? ..() : FALSE
+
+/datum/nanite_program/brain_heal/active_effect()
+ host_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1)
+ if(iscarbon(host_mob) && prob(10))
+ var/mob/living/carbon/carbon_host = host_mob
+ carbon_host.cure_trauma_type(resilience = TRAUMA_RESILIENCE_BASIC)
+
+#define NANITE_BLOOD_RESTORE_DEFAULT 2
+
+/datum/nanite_program/blood_restoring
+ name = "Blood Regeneration"
+ desc = "The nanites stimulate and boost blood cell production in the host."
+ use_rate = 1
+ rogue_types = list(/datum/nanite_program/suffocating)
+ ///The amount of blood that we restore every active effect tick.
+ var/blood_restore_amount = NANITE_BLOOD_RESTORE_DEFAULT
+
+/datum/nanite_program/blood_restoring/check_conditions()
+ if(!iscarbon(host_mob))
+ return FALSE
+ var/mob/living/carbon/carbon_host = host_mob
+ if(carbon_host.blood_volume >= BLOOD_VOLUME_SAFE)
+ return FALSE
+ return ..()
+
+/datum/nanite_program/blood_restoring/active_effect()
+ if(!iscarbon(host_mob))
+ return
+ var/mob/living/carbon/carbon_host = host_mob
+ carbon_host.blood_volume += blood_restore_amount
+
+#undef NANITE_BLOOD_RESTORE_DEFAULT
+
+/datum/nanite_program/repairing
+ name = "Mechanical Repair"
+ desc = "The nanites fix damage in the host's mechanical limbs."
+ use_rate = 0.5
+ rogue_types = list(/datum/nanite_program/necrotic)
+
+/datum/nanite_program/repairing/check_conditions()
+ if(!host_mob.getBruteLoss() && !host_mob.getFireLoss())
+ return FALSE
+
+ if(!iscarbon(host_mob))
+ if(!(host_mob.mob_biotypes & MOB_ROBOTIC))
+ return FALSE
+ return ..()
+
+ var/mob/living/carbon/carbon_host = host_mob
+ var/list/parts = carbon_host.get_damaged_bodyparts(brute = TRUE, burn = TRUE, required_bodytype = BODYTYPE_ROBOTIC)
+ if(!parts.len)
+ return FALSE
+ return ..()
+
+/datum/nanite_program/repairing/active_effect(mob/living/M)
+ if(!iscarbon(host_mob))
+ host_mob.adjustBruteLoss(-1.5, TRUE)
+ host_mob.adjustFireLoss(-1.5, TRUE)
+ return
+ host_mob.heal_overall_damage(brute = 1.5, brute = 1.5, required_bodytype = BODYTYPE_ROBOTIC)
+
+/datum/nanite_program/purging_advanced
+ name = "Selective Blood Purification"
+ desc = "The nanites purge toxins and dangerous chemicals from the host's bloodstream, while ignoring beneficial chemicals. \
+ The added processing power required to analyze the chemicals severely increases the nanite consumption rate."
+ use_rate = 2
+ rogue_types = list(/datum/nanite_program/suffocating, /datum/nanite_program/necrotic)
+
+/datum/nanite_program/purging_advanced/check_conditions()
+ var/foreign_reagent = FALSE
+ for(var/datum/reagent/toxin/toxic_reagents in host_mob.reagents.reagent_list)
+ foreign_reagent = TRUE
+ break
+ if(!host_mob.getToxLoss() && !foreign_reagent)
+ return FALSE
+ return ..()
+
+/datum/nanite_program/purging_advanced/active_effect()
+ host_mob.adjustToxLoss(-1)
+ for(var/datum/reagent/toxin/toxic_reagents in host_mob.reagents.reagent_list)
+ host_mob.reagents.remove_reagent(toxic_reagents.type, 1)
+
+/datum/nanite_program/brain_heal_advanced
+ name = "Neural Reimaging"
+ desc = "The nanites are able to backup and restore the host's neural connections, potentially replacing entire chunks of missing or damaged brain matter."
+ use_rate = 3
+ rogue_types = list(/datum/nanite_program/brain_decay, /datum/nanite_program/brain_misfire)
+
+/datum/nanite_program/brain_heal_advanced/check_conditions()
+ var/problems = FALSE
+ if(iscarbon(host_mob))
+ var/mob/living/carbon/carbon_host = host_mob
+ if(length(carbon_host.get_traumas()))
+ problems = TRUE
+ if(host_mob.get_organ_loss(ORGAN_SLOT_BRAIN) > 0)
+ problems = TRUE
+ return problems ? ..() : FALSE
+
+/datum/nanite_program/brain_heal_advanced/active_effect()
+ host_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, -2)
+ if(iscarbon(host_mob) && prob(10))
+ var/mob/living/carbon/carbon_host = host_mob
+ carbon_host.cure_trauma_type(resilience = TRAUMA_RESILIENCE_LOBOTOMY)
+
+/datum/nanite_program/defib
+ name = "Defibrillation"
+ desc = "The nanites shock the host's heart when triggered, bringing them back to life if the body can sustain it."
+ can_trigger = TRUE
+ trigger_cost = 25
+ trigger_cooldown = 120
+ rogue_types = list(/datum/nanite_program/shocking)
+
+/datum/nanite_program/defib/on_trigger(comm_message)
+ host_mob.notify_revival("Your heart is being defibrillated by nanites. Re-enter your corpse if you want to be revived!")
+ addtimer(CALLBACK(src, PROC_REF(start_defibrilation)), 5 SECONDS)
+
+/datum/nanite_program/defib/proc/check_revivable()
+ if(!iscarbon(host_mob))
+ return FALSE
+ var/mob/living/carbon/carbon_host = host_mob
+ return carbon_host.can_defib()
+
+/datum/nanite_program/defib/proc/start_defibrilation()
+ playsound(host_mob, 'sound/machines/defib_charge.ogg', 50, FALSE)
+ addtimer(CALLBACK(src, PROC_REF(perform_defibrilation)), 3 SECONDS)
+
+/datum/nanite_program/defib/proc/perform_defibrilation()
+ var/mob/living/carbon/carbon_host = host_mob
+ playsound(carbon_host, 'sound/machines/defib_zap.ogg', 50, FALSE)
+ if(!check_revivable())
+ playsound(carbon_host, 'sound/machines/defib_failed.ogg', 50, FALSE)
+ return
+ playsound(carbon_host, 'sound/machines/defib_success.ogg', 50, FALSE)
+ carbon_host.set_heartattack(FALSE)
+ carbon_host.revive()
+ carbon_host.emote("gasp")
+ carbon_host.set_timed_status_effect(10 SECONDS, /datum/status_effect/jitter, only_if_higher = TRUE)
+ SEND_SIGNAL(carbon_host, COMSIG_LIVING_MINOR_SHOCK)
+ log_game("[carbon_host] has been successfully defibrillated by nanites.")
diff --git a/massmeta/features/nanites/code/programs/protocols.dm b/massmeta/features/nanites/code/programs/protocols.dm
new file mode 100644
index 0000000000000..0d7c0f2d2b789
--- /dev/null
+++ b/massmeta/features/nanites/code/programs/protocols.dm
@@ -0,0 +1,320 @@
+/datum/nanite_program/protocol
+ name = "Nanite Protocol"
+
+ ///If specified, you may only have one of these protocol types active at once.
+ ///Selection: (NANITE_PROTOCOL_REPLICATION | NANITE_PROTOCOL_STORAGE)
+ var/protocol_class = NONE
+
+/datum/nanite_program/protocol/on_add(datum/component/nanites/_nanites)
+ . = ..()
+ nanites.protocols += src
+
+/datum/nanite_program/protocol/Destroy()
+ if(nanites)
+ nanites.protocols -= src
+ return ..()
+
+/**
+ * Replication Protocols
+ */
+/datum/nanite_program/protocol/kickstart
+ name = "Kickstart Protocol"
+ desc = "Replication Protocol: the nanites focus on early growth, heavily boosting replication rate for a few minutes after the initial implantation."
+ use_rate = 0
+ rogue_types = list(/datum/nanite_program/necrotic)
+ protocol_class = NANITE_PROTOCOL_REPLICATION
+ var/boost_duration = 2 MINUTES
+
+/datum/nanite_program/protocol/kickstart/check_conditions()
+ if(!(world.time < nanites.start_time + boost_duration))
+ return FALSE
+ return ..()
+
+/datum/nanite_program/protocol/kickstart/active_effect()
+ nanites.adjust_nanites(null, 3.5)
+
+/datum/nanite_program/protocol/factory
+ name = "Factory Protocol"
+ desc = "Replication Protocol: the nanites build a factory matrix within the host, gradually increasing replication speed over time. \
+ The factory decays if the protocol is not active, or if the nanites are disrupted by shocks or EMPs."
+ use_rate = 0
+ rogue_types = list(/datum/nanite_program/necrotic)
+ protocol_class = NANITE_PROTOCOL_REPLICATION
+ var/factory_efficiency = 0
+ var/max_efficiency = 1000 //Goes up to 2 bonus regen per tick after 16 minutes and 40 seconds
+
+/datum/nanite_program/protocol/factory/on_process()
+ if(!activated || !check_conditions())
+ factory_efficiency = max(0, factory_efficiency - 5)
+ return ..()
+
+/datum/nanite_program/protocol/factory/on_emp(severity)
+ . = ..()
+ factory_efficiency = max(0, factory_efficiency - 300)
+
+/datum/nanite_program/protocol/factory/on_shock(shock_damage)
+ . = ..()
+ factory_efficiency = max(0, factory_efficiency - 200)
+
+/datum/nanite_program/protocol/factory/on_minor_shock()
+ . = ..()
+ factory_efficiency = max(0, factory_efficiency - 100)
+
+/datum/nanite_program/protocol/factory/active_effect()
+ factory_efficiency = min(factory_efficiency + 1, max_efficiency)
+ nanites.adjust_nanites(null, round(0.002 * factory_efficiency, 0.1))
+
+/datum/nanite_program/protocol/tinker
+ name = "Tinker Protocol"
+ desc = "Replication Protocol: the nanites learn to use metallic material in the host's bloodstream and stomach to speed up the replication process."
+ use_rate = 0
+ rogue_types = list(/datum/nanite_program/necrotic)
+ protocol_class = NANITE_PROTOCOL_REPLICATION
+ var/boost = 2
+ var/list/datum/reagent/valid_reagents = list(
+ /datum/reagent/iron,
+ /datum/reagent/copper,
+ /datum/reagent/gold,
+ /datum/reagent/silver,
+ /datum/reagent/mercury,
+ /datum/reagent/aluminium,
+ /datum/reagent/silicon,
+ )
+
+/datum/nanite_program/protocol/tinker/check_conditions()
+ if(!nanites.host_mob.reagents)
+ return FALSE
+
+ var/found_reagent = FALSE
+
+ var/datum/reagents/R = nanites.host_mob.reagents
+ for(var/VR in valid_reagents)
+ if(R.has_reagent(VR, 0.5))
+ R.remove_reagent(VR, 0.5)
+ found_reagent = TRUE
+ break
+ if(!found_reagent)
+ return FALSE
+ return ..()
+
+/datum/nanite_program/protocol/tinker/active_effect()
+ nanites.adjust_nanites(null, boost)
+
+/datum/nanite_program/protocol/offline
+ name = "Offline Production Protocol"
+ desc = "Replication Protocol: while the host is asleep or otherwise unconcious, the nanites exploit the reduced interference to replicate more quickly."
+ use_rate = 0
+ rogue_types = list(/datum/nanite_program/necrotic)
+ protocol_class = NANITE_PROTOCOL_REPLICATION
+ var/boost = 3
+
+/datum/nanite_program/protocol/offline/check_conditions()
+ if(nanites.host_mob.stat == CONSCIOUS)
+ return FALSE
+ return ..()
+
+/datum/nanite_program/protocol/offline/active_effect()
+ nanites.adjust_nanites(null, boost)
+
+/**
+ * Storage Protocols
+ */
+/datum/nanite_program/protocol/hive
+ name = "Hive Protocol"
+ desc = "Storage Protocol: the nanites use a more efficient grid arrangment for volume storage, increasing maximum volume by 250."
+ use_rate = 0
+ rogue_types = list(/datum/nanite_program/necrotic)
+ protocol_class = NANITE_PROTOCOL_STORAGE
+ ///How much extra volume the protocol gives the nanite user.
+ var/extra_volume = 250
+
+/datum/nanite_program/protocol/hive/enable_passive_effect()
+ . = ..()
+ nanites.set_max_volume(null, nanites.max_nanites + extra_volume)
+
+/datum/nanite_program/protocol/hive/disable_passive_effect()
+ . = ..()
+ nanites.set_max_volume(null, nanites.max_nanites - extra_volume)
+
+/datum/nanite_program/protocol/zip
+ name = "Zip Protocol"
+ desc = "Storage Protocol: the nanites are disassembled and compacted when unused, increasing the maximum volume to 1000. However, the process slows down their replication rate slightly."
+ use_rate = 0.2
+ rogue_types = list(/datum/nanite_program/necrotic)
+ protocol_class = NANITE_PROTOCOL_STORAGE
+ ///How much extra volume the protocol gives the nanite user.
+ var/extra_volume = 500
+
+/datum/nanite_program/protocol/zip/enable_passive_effect()
+ . = ..()
+ nanites.set_max_volume(null, nanites.max_nanites + extra_volume)
+
+/datum/nanite_program/protocol/zip/disable_passive_effect()
+ . = ..()
+ nanites.set_max_volume(null, nanites.max_nanites - extra_volume)
+
+/datum/nanite_program/protocol/free_range
+ name = "Free-range Protocol"
+ desc = "Storage Protocol: the nanites discard their default storage protocols in favour of a cheaper and more organic approach. Reduces maximum volume to 250, but increases the replication rate by 0.5."
+ use_rate = -0.5
+ rogue_types = list(/datum/nanite_program/necrotic)
+ protocol_class = NANITE_PROTOCOL_STORAGE
+ ///How much extra volume the protocol gives the nanite user. Since this is negative, we take away.
+ var/extra_volume = -250
+
+/datum/nanite_program/protocol/free_range/enable_passive_effect()
+ . = ..()
+ nanites.set_max_volume(null, nanites.max_nanites + extra_volume)
+
+/datum/nanite_program/protocol/free_range/disable_passive_effect()
+ . = ..()
+ nanites.set_max_volume(null, nanites.max_nanites - extra_volume)
+
+/datum/nanite_program/protocol/unsafe_storage
+ name = "S.L.O. Protocol"
+ desc = "Storage Protocol: 'S.L.O.P.', or Storage Level Override Protocol, completely disables the safety measures normally present in nanites, \
+ allowing them to reach a whopping maximum volume level of 2000, but at the risk of causing damage to the host at nanite concentrations above the standard limit of 500."
+ use_rate = 0
+ rogue_types = list(/datum/nanite_program/necrotic)
+ protocol_class = NANITE_PROTOCOL_STORAGE
+ ///How much extra volume the protocol gives the nanite user.
+ var/extra_volume = 1500
+ ///The timer between warnings.
+ COOLDOWN_DECLARE(next_warning)
+
+ var/list/volume_warnings_stage_1 = list(
+ "You feel a dull pain in your abdomen.",
+ "You feel a tickling sensation in your abdomen.",
+ )
+ var/list/volume_warnings_stage_2 = list(
+ "You feel a dull pain in your stomach.",
+ "You feel a dull pain when breathing.",
+ "Your stomach grumbles.",
+ "You feel a tickling sensation in your throat.",
+ "You feel a tickling sensation in your lungs.",
+ "You feel a tickling sensation in your stomach.",
+ "Your lungs feel stiff.",
+ )
+ var/list/volume_warnings_stage_3 = list(
+ "You feel a dull pain in your chest.",
+ "You hear a faint buzzing coming from nowhere.",
+ "You hear a faint buzzing inside your head.",
+ "Your head aches.",
+ )
+ var/list/volume_warnings_stage_4 = list(
+ "You feel a dull pain in your ears.",
+ "You feel a dull pain behind your eyes.",
+ "You hear a loud, echoing buzz inside your ears.",
+ "You feel dizzy.",
+ "You feel an itch coming from behind your eyes.",
+ "Your eardrums itch.",
+ "You see tiny grey motes drifting in your field of view.",
+ )
+ var/list/volume_warnings_stage_5 = list(
+ "You feel sick.",
+ "You feel a dull pain from every part of your body.",
+ "You feel nauseous.",
+ )
+ var/list/volume_warnings_stage_6 = list(
+ "Your skin itches and burns.",
+ "Your muscles ache.",
+ "You feel tired.",
+ "You feel something skittering under your skin.",
+ )
+
+/datum/nanite_program/protocol/unsafe_storage/enable_passive_effect()
+ . = ..()
+ nanites.set_max_volume(null, nanites.max_nanites + extra_volume)
+
+/datum/nanite_program/protocol/unsafe_storage/disable_passive_effect()
+ . = ..()
+ nanites.set_max_volume(null, nanites.max_nanites - extra_volume)
+
+/datum/nanite_program/protocol/unsafe_storage/active_effect()
+ if(!iscarbon(host_mob))
+ if(nanites.nanite_volume < 500 || !prob(10))
+ return
+ return host_mob.adjustBruteLoss(((max(nanites.nanite_volume - 450, 0) / 450) ** 2 ) * 0.5) // 0.5 -> 2 -> 4.5 -> 8 damage per successful tick
+
+ if(nanites.nanite_volume < 500)
+ return
+
+ var/current_stage = 0
+ var/list/organs_to_damage = list()
+
+ if(nanites.nanite_volume > 500) //Liver is the main hub of nanite replication and the first to be threatened by excess volume
+ if(prob(10))
+ organs_to_damage += ORGAN_SLOT_LIVER
+ current_stage++
+ if(nanites.nanite_volume > 750) //Extra volume spills out in other central organs
+ if(prob(10))
+ organs_to_damage += ORGAN_SLOT_STOMACH
+ if(prob(10))
+ organs_to_damage += ORGAN_SLOT_LUNGS
+ current_stage++
+ if(nanites.nanite_volume > 1000) //Extra volume spills out in more critical organs
+ if(prob(20))
+ organs_to_damage += ORGAN_SLOT_HEART
+ current_stage++
+ if(nanites.nanite_volume > 1250) //Excess nanites start invading smaller organs for more space, including sensory organs
+ if(prob(30))
+ organs_to_damage += ORGAN_SLOT_EYES
+ current_stage++
+ if(nanites.nanite_volume > 1500) //Nanites start spilling into the bloodstream, causing toxicity
+ if(prob(15))
+ host_mob.adjustToxLoss(0.5, TRUE, forced = TRUE) //Not healthy for slimepeople either
+ current_stage++
+ if(nanites.nanite_volume > 1750) //Nanites have almost reached their physical limit, and the pressure itself starts causing tissue damage
+ if(prob(15))
+ host_mob.adjustBruteLoss(0.75, TRUE)
+ current_stage++
+
+ if(length(organs_to_damage))
+ var/mob/living/carbon/carbon_host = host_mob
+ for(var/organ_slot in organs_to_damage)
+ var/obj/item/organ/damaged_organ = carbon_host.get_organ_slot(organ_slot)
+ if(damaged_organ)
+ damaged_organ.apply_organ_damage(0.75)
+
+ volume_warning(current_stage)
+
+#define MINIMUM_WARNING_COOLDOWN (12 SECONDS)
+#define MAXIMUM_WARNING_COOLDOWN (35 SECONDS)
+
+/datum/nanite_program/protocol/unsafe_storage/proc/volume_warning(tier)
+ if(!COOLDOWN_FINISHED(src, next_warning))
+ return
+
+ var/main_warning
+ var/extra_warning
+
+ switch(tier)
+ if(1)
+ main_warning = pick(volume_warnings_stage_1)
+ extra_warning = null
+ if(2)
+ main_warning = pick(volume_warnings_stage_2)
+ extra_warning = pick(volume_warnings_stage_1)
+ if(3)
+ main_warning = pick(volume_warnings_stage_3)
+ extra_warning = pick(volume_warnings_stage_1 + volume_warnings_stage_2)
+ if(4)
+ main_warning = pick(volume_warnings_stage_4)
+ extra_warning = pick(volume_warnings_stage_1 + volume_warnings_stage_2 + volume_warnings_stage_3)
+ if(5)
+ main_warning = pick(volume_warnings_stage_5)
+ extra_warning = pick(volume_warnings_stage_1 + volume_warnings_stage_2 + volume_warnings_stage_3 + volume_warnings_stage_4)
+ if(6)
+ main_warning = pick(volume_warnings_stage_6)
+ extra_warning = pick(volume_warnings_stage_1 + volume_warnings_stage_2 + volume_warnings_stage_3 + volume_warnings_stage_4 + volume_warnings_stage_5)
+
+ if(prob(35))
+ to_chat(host_mob, span_warning("[main_warning]"))
+ COOLDOWN_START(src, next_warning, rand(MINIMUM_WARNING_COOLDOWN, MAXIMUM_WARNING_COOLDOWN))
+ return
+ if(extra_warning)
+ to_chat(host_mob, span_warning("[extra_warning]"))
+ COOLDOWN_START(src, next_warning, rand(MINIMUM_WARNING_COOLDOWN, MAXIMUM_WARNING_COOLDOWN))
+
+#undef MINIMUM_WARNING_COOLDOWN
+#undef MAXIMUM_WARNING_COOLDOWN
diff --git a/massmeta/features/nanites/code/programs/rogue.dm b/massmeta/features/nanites/code/programs/rogue.dm
new file mode 100644
index 0000000000000..8be1df64f8992
--- /dev/null
+++ b/massmeta/features/nanites/code/programs/rogue.dm
@@ -0,0 +1,119 @@
+/datum/nanite_program/glitch
+ name = "Glitch"
+ desc = "A heavy software corruption that causes nanites to gradually break down."
+ use_rate = 1.5
+ unique = FALSE
+ rogue_types = list()
+
+//Generic body-affecting programs will decay into this
+/datum/nanite_program/necrotic
+ name = "Necrosis"
+ desc = "The nanites attack internal tissues indiscriminately, causing widespread damage."
+ use_rate = 0.75
+ unique = FALSE
+ rogue_types = list(/datum/nanite_program/glitch)
+
+/datum/nanite_program/necrotic/active_effect()
+ host_mob.adjustBruteLoss(0.75, TRUE)
+ if(prob(1))
+ to_chat(host_mob, span_warning("You feel a mild ache from somewhere inside you."))
+
+//Programs that don't directly interact with the body will decay into this
+/datum/nanite_program/toxic
+ name = "Toxin Buildup"
+ desc = "The nanites cause a slow but constant toxin buildup inside the host."
+ use_rate = 0.25
+ unique = FALSE
+ rogue_types = list(/datum/nanite_program/glitch)
+
+/datum/nanite_program/toxic/active_effect()
+ host_mob.adjustToxLoss(0.5)
+ if(prob(1))
+ to_chat(host_mob, span_warning("You feel a bit sick."))
+
+//Generic blood-affecting programs will decay into this
+/datum/nanite_program/suffocating
+ name = "Hypoxemia"
+ desc = "The nanites prevent the host's blood from absorbing oxygen efficiently."
+ use_rate = 0.75
+ unique = FALSE
+ rogue_types = list(/datum/nanite_program/glitch)
+
+/datum/nanite_program/suffocating/active_effect()
+ host_mob.adjustOxyLoss(3, 0)
+ if(prob(1))
+ to_chat(host_mob, span_warning("You feel short of breath."))
+
+//Generic brain-affecting programs will decay into this
+/datum/nanite_program/brain_decay
+ name = "Neuro-Necrosis"
+ desc = "The nanites seek and attack brain cells, causing extensive neural damage to the host."
+ use_rate = 0.75
+ unique = FALSE
+ rogue_types = list(/datum/nanite_program/necrotic)
+
+/datum/nanite_program/brain_decay/active_effect()
+ if(prob(4))
+ host_mob.set_hallucinations_if_lower(1.5 SECONDS)
+ host_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, 1)
+
+//Generic brain-affecting programs can also decay into this
+/datum/nanite_program/brain_misfire
+ name = "Brain Misfire"
+ desc = "The nanites interfere with neural pathways, causing minor psychological disturbances."
+ use_rate = 0.50
+ unique = FALSE
+ rogue_types = list(/datum/nanite_program/brain_decay)
+
+/datum/nanite_program/brain_misfire/active_effect()
+ if(!prob(10))
+ return
+ switch(rand(1,4))
+ if(1)
+ host_mob.set_hallucinations(1.5 SECONDS)
+ if(2)
+ host_mob.adjust_timed_status_effect(3 SECONDS, /datum/status_effect/confusion)
+ if(3)
+ host_mob.adjust_timed_status_effect(3 SECONDS, /datum/status_effect/dizziness)
+ if(4)
+ host_mob.adjust_timed_status_effect(3 SECONDS, /datum/status_effect/speech/slurring/drunk)
+
+//Generic skin-affecting programs will decay into this
+/datum/nanite_program/skin_decay
+ name = "Dermalysis"
+ desc = "The nanites attack skin cells, causing irritation, rashes, and minor damage."
+ use_rate = 0.25
+ unique = FALSE
+ rogue_types = list(/datum/nanite_program/necrotic)
+
+/datum/nanite_program/skin_decay/active_effect()
+ host_mob.adjustBruteLoss(0.25)
+ if(prob(5)) //itching
+ var/picked_bodypart = pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)
+ var/obj/item/bodypart/bodypart = host_mob.get_bodypart(picked_bodypart)
+ var/can_scratch = !host_mob.incapacitated() && get_location_accessible(host_mob, picked_bodypart)
+
+ host_mob.visible_message(
+ "[can_scratch ? span_warning("[host_mob] scratches [host_mob.p_their()] [bodypart.name].") : ""]",
+ span_warning("Your [bodypart.name] itches. [can_scratch ? " You scratch it." : ""]"))
+
+//Generic nerve-affecting programs will decay into this
+/datum/nanite_program/nerve_decay
+ name = "Nerve Decay"
+ desc = "The nanites attack the host's nerves, causing lack of coordination and short bursts of paralysis."
+ use_rate = 1
+ unique = FALSE
+ rogue_types = list(/datum/nanite_program/necrotic)
+
+/datum/nanite_program/nerve_decay/active_effect()
+ if(prob(5))
+ to_chat(host_mob, span_warning("You feel unbalanced!"))
+ host_mob.adjust_timed_status_effect(5 SECONDS, /datum/status_effect/confusion)
+ return
+ if(prob(4))
+ to_chat(host_mob, span_warning("You can't feel your hands!"))
+ host_mob.drop_all_held_items()
+ return
+ if(prob(4))
+ to_chat(host_mob, span_warning("You can't feel your legs!"))
+ host_mob.Paralyze(30)
diff --git a/massmeta/features/nanites/code/programs/sensor.dm b/massmeta/features/nanites/code/programs/sensor.dm
new file mode 100644
index 0000000000000..7fc75b58205b9
--- /dev/null
+++ b/massmeta/features/nanites/code/programs/sensor.dm
@@ -0,0 +1,309 @@
+/datum/nanite_program/sensor
+ name = NANITES_CATEGORY_SENSOR
+ desc = "These nanites send a signal code when a certain condition is met."
+ unique = FALSE
+ var/can_rule = FALSE
+
+/datum/nanite_program/sensor/register_extra_settings()
+ extra_settings[NES_SENT_CODE] = new /datum/nanite_extra_setting/number(0, 1, 9999)
+
+/datum/nanite_program/sensor/proc/check_event()
+ return FALSE
+
+/datum/nanite_program/sensor/proc/send_code()
+ if(activated)
+ var/datum/nanite_extra_setting/ES = extra_settings[NES_SENT_CODE]
+ SEND_SIGNAL(host_mob, COMSIG_NANITE_SIGNAL, ES.value, "a [name] program")
+
+/datum/nanite_program/sensor/active_effect()
+ if(check_event())
+ send_code()
+
+/datum/nanite_program/sensor/proc/make_rule(datum/nanite_program/target)
+ return
+
+/datum/nanite_program/sensor/repeat
+ name = "Signal Repeater"
+ desc = "When triggered, sends another signal to the nanites, optionally with a delay."
+ can_trigger = TRUE
+ trigger_cost = 0
+ trigger_cooldown = 10
+
+/datum/nanite_program/sensor/repeat/register_extra_settings()
+ . = ..()
+ extra_settings[NES_DELAY] = new /datum/nanite_extra_setting/number(0, 0, 3600, "s")
+
+/datum/nanite_program/sensor/repeat/on_trigger(comm_message)
+ var/datum/nanite_extra_setting/ES = extra_settings[NES_DELAY]
+ addtimer(CALLBACK(src, PROC_REF(send_code)), ES.get_value() * 10)
+
+/datum/nanite_program/sensor/relay_repeat
+ name = "Relay Signal Repeater"
+ desc = "When triggered, sends another signal to a relay channel, optionally with a delay."
+ can_trigger = TRUE
+ trigger_cost = 0
+ trigger_cooldown = 10
+
+/datum/nanite_program/sensor/relay_repeat/register_extra_settings()
+ . = ..()
+ extra_settings[NES_RELAY_CHANNEL] = new /datum/nanite_extra_setting/number(1, 1, 9999)
+ extra_settings[NES_DELAY] = new /datum/nanite_extra_setting/number(0, 0, 3600, "s")
+
+/datum/nanite_program/sensor/relay_repeat/on_trigger(comm_message)
+ var/datum/nanite_extra_setting/ES = extra_settings[NES_DELAY]
+ addtimer(CALLBACK(src, PROC_REF(send_code)), ES.get_value() * 10)
+
+/datum/nanite_program/sensor/relay_repeat/send_code()
+ var/datum/nanite_extra_setting/relay = extra_settings[NES_RELAY_CHANNEL]
+ if(activated && relay.get_value())
+ for(var/datum/nanite_program/relay/relays as anything in SSnanites.nanite_relays)
+ var/datum/nanite_extra_setting/code = extra_settings[NES_SENT_CODE]
+ relays.relay_signal(code.get_value(), relay.get_value(), "a [name] program")
+
+/datum/nanite_program/sensor/health
+ name = "Health Sensor"
+ desc = "The nanites receive a signal when the host's health is above/below a target percentage."
+ can_rule = TRUE
+ var/spent = FALSE
+
+/datum/nanite_program/sensor/health/register_extra_settings()
+ . = ..()
+ extra_settings[NES_HEALTH_PERCENT] = new /datum/nanite_extra_setting/number(50, -99, 100, "%")
+ extra_settings[NES_DIRECTION] = new /datum/nanite_extra_setting/boolean(TRUE, "Above", "Below")
+
+/datum/nanite_program/sensor/health/check_event()
+ var/health_percent = host_mob.health / host_mob.maxHealth * 100
+ var/datum/nanite_extra_setting/percent = extra_settings[NES_HEALTH_PERCENT]
+ var/datum/nanite_extra_setting/direction = extra_settings[NES_DIRECTION]
+ var/detected = FALSE
+ if(direction.get_value())
+ if(health_percent >= percent.get_value())
+ detected = TRUE
+ else
+ if(health_percent < percent.get_value())
+ detected = TRUE
+
+ if(detected)
+ if(!spent)
+ spent = TRUE
+ return TRUE
+ return FALSE
+ else
+ spent = FALSE
+ return FALSE
+
+/datum/nanite_program/sensor/health/make_rule(datum/nanite_program/target)
+ var/datum/nanite_rule/health/rule = new(target)
+ var/datum/nanite_extra_setting/direction = extra_settings[NES_DIRECTION]
+ var/datum/nanite_extra_setting/percent = extra_settings[NES_HEALTH_PERCENT]
+ rule.above = direction.get_value()
+ rule.threshold = percent.get_value()
+ return rule
+
+/datum/nanite_program/sensor/crit
+ name = "Critical Health Sensor"
+ desc = "The nanites receive a signal when the host first reaches critical health."
+ can_rule = TRUE
+ var/spent = FALSE
+
+/datum/nanite_program/sensor/crit/check_event()
+ if(HAS_TRAIT(host_mob, TRAIT_CRITICAL_CONDITION))
+ if(spent)
+ return FALSE
+ spent = TRUE
+ return TRUE
+ spent = FALSE
+ return FALSE
+
+
+/datum/nanite_program/sensor/crit/make_rule(datum/nanite_program/target)
+ var/datum/nanite_rule/crit/rule = new(target)
+ return rule
+
+/datum/nanite_program/sensor/death
+ name = "Death Sensor"
+ desc = "The nanites receive a signal when they detect the host is dead."
+ can_rule = TRUE
+
+/datum/nanite_program/sensor/death/on_death(gibbed)
+ send_code()
+
+/datum/nanite_program/sensor/death/make_rule(datum/nanite_program/target)
+ var/datum/nanite_rule/death/rule = new(target)
+ return rule
+
+/datum/nanite_program/sensor/nanite_volume
+ name = "Nanite Volume Sensor"
+ desc = "The nanites receive a signal when the nanite supply is above/below a certain percentage."
+ can_rule = TRUE
+ var/spent = FALSE
+
+/datum/nanite_program/sensor/nanite_volume/register_extra_settings()
+ . = ..()
+ extra_settings[NES_NANITE_PERCENT] = new /datum/nanite_extra_setting/number(50, -99, 100, "%")
+ extra_settings[NES_DIRECTION] = new /datum/nanite_extra_setting/boolean(TRUE, "Above", "Below")
+
+/datum/nanite_program/sensor/nanite_volume/check_event()
+ var/nanite_percent = (nanites.nanite_volume - nanites.safety_threshold)/(nanites.max_nanites - nanites.safety_threshold)*100
+ var/datum/nanite_extra_setting/percent = extra_settings[NES_NANITE_PERCENT]
+ var/datum/nanite_extra_setting/direction = extra_settings[NES_DIRECTION]
+ var/detected = FALSE
+ if(direction.get_value())
+ if(nanite_percent >= percent.get_value())
+ detected = TRUE
+ else
+ if(nanite_percent < percent.get_value())
+ detected = TRUE
+
+ if(detected)
+ if(!spent)
+ spent = TRUE
+ return TRUE
+ return FALSE
+ else
+ spent = FALSE
+ return FALSE
+
+/datum/nanite_program/sensor/nanite_volume/make_rule(datum/nanite_program/target)
+ var/datum/nanite_rule/nanites/rule = new(target)
+ var/datum/nanite_extra_setting/direction = extra_settings[NES_DIRECTION]
+ var/datum/nanite_extra_setting/percent = extra_settings[NES_NANITE_PERCENT]
+ rule.above = direction.get_value()
+ rule.threshold = percent.get_value()
+ return rule
+
+/datum/nanite_program/sensor/damage
+ name = "Damage Sensor"
+ desc = "The nanites receive a signal when a host's specific damage type is above/below a target value."
+ can_rule = TRUE
+ var/spent = FALSE
+
+/datum/nanite_program/sensor/damage/register_extra_settings()
+ . = ..()
+ extra_settings[NES_DAMAGE_TYPE] = new /datum/nanite_extra_setting/type(BRUTE, list(BRUTE, BURN, TOX, OXY))
+ extra_settings[NES_DAMAGE] = new /datum/nanite_extra_setting/number(50, 0, 500)
+ extra_settings[NES_DIRECTION] = new /datum/nanite_extra_setting/boolean(TRUE, "Above", "Below")
+
+/datum/nanite_program/sensor/damage/check_event()
+ var/reached_threshold = FALSE
+ var/datum/nanite_extra_setting/type = extra_settings[NES_DAMAGE_TYPE]
+ var/datum/nanite_extra_setting/damage = extra_settings[NES_DAMAGE]
+ var/datum/nanite_extra_setting/direction = extra_settings[NES_DIRECTION]
+ var/check_above = direction.get_value()
+ var/damage_amt = 0
+ switch(type.get_value())
+ if(BRUTE)
+ damage_amt = host_mob.getBruteLoss()
+ if(BURN)
+ damage_amt = host_mob.getFireLoss()
+ if(TOX)
+ damage_amt = host_mob.getToxLoss()
+ if(OXY)
+ damage_amt = host_mob.getOxyLoss()
+
+ if(check_above)
+ if(damage_amt >= damage.get_value())
+ reached_threshold = TRUE
+ else
+ if(damage_amt < damage.get_value())
+ reached_threshold = TRUE
+
+ if(reached_threshold)
+ if(!spent)
+ spent = TRUE
+ return TRUE
+ return FALSE
+ else
+ spent = FALSE
+ return FALSE
+
+/datum/nanite_program/sensor/damage/make_rule(datum/nanite_program/target)
+ var/datum/nanite_rule/damage/rule = new(target)
+ var/datum/nanite_extra_setting/direction = extra_settings[NES_DIRECTION]
+ var/datum/nanite_extra_setting/damage_type = extra_settings[NES_DAMAGE_TYPE]
+ var/datum/nanite_extra_setting/damage = extra_settings[NES_DAMAGE]
+ rule.above = direction.get_value()
+ rule.threshold = damage.get_value()
+ rule.damage_type = damage_type.get_value()
+ return rule
+
+/datum/nanite_program/sensor/voice
+ name = "Voice Sensor"
+ desc = "Sends a signal when the nanites hear a determined word or sentence."
+
+/datum/nanite_program/sensor/voice/register_extra_settings()
+ . = ..()
+ extra_settings[NES_SENTENCE] = new /datum/nanite_extra_setting/text("")
+ extra_settings[NES_INCLUSIVE_MODE] = new /datum/nanite_extra_setting/boolean(TRUE, "Inclusive", "Exclusive")
+
+/datum/nanite_program/sensor/voice/on_mob_add()
+ . = ..()
+ RegisterSignal(host_mob, COMSIG_MOVABLE_HEAR, PROC_REF(on_hear))
+
+/datum/nanite_program/sensor/voice/on_mob_remove()
+ UnregisterSignal(host_mob, COMSIG_MOVABLE_HEAR, PROC_REF(on_hear))
+ return ..()
+
+/datum/nanite_program/sensor/voice/proc/on_hear(datum/source, list/hearing_args)
+ var/datum/nanite_extra_setting/sentence = extra_settings[NES_SENTENCE]
+ var/datum/nanite_extra_setting/inclusive = extra_settings[NES_INCLUSIVE_MODE]
+ if(!sentence.get_value())
+ return
+ if(inclusive.get_value())
+ if(findtext(hearing_args[HEARING_RAW_MESSAGE], sentence.get_value()))
+ send_code()
+ else
+ if(lowertext(hearing_args[HEARING_RAW_MESSAGE]) == lowertext(sentence.get_value()))
+ send_code()
+
+/datum/nanite_program/sensor/species
+ name = "Species Sensor"
+ desc = "When triggered, the nanites scan the host to determine their species and output a signal depending on the conditions set in the settings."
+ can_trigger = TRUE
+ trigger_cost = 0
+ trigger_cooldown = 5
+
+ ///List of all species we can add special sensors for.
+ var/static/list/allowed_species = list(
+ "Human" = /datum/species/human,
+ "Lizard" = /datum/species/lizard,
+ "Moth" = /datum/species/moth,
+ "Ethereal" = /datum/species/ethereal,
+ //"Beefeman" = /datum/species/beefman, //No beefeman
+ "Pod" = /datum/species/pod,
+ "Fly" = /datum/species/fly,
+ "Jelly" = /datum/species/jelly,
+ )
+
+/datum/nanite_program/sensor/species/register_extra_settings()
+ . = ..()
+ var/list/species_types = list()
+ for(var/name in allowed_species)
+ species_types += name
+ species_types += "Other"
+ extra_settings[NES_RACE] = new /datum/nanite_extra_setting/type("Human", species_types)
+ extra_settings[NES_MODE] = new /datum/nanite_extra_setting/boolean(TRUE, "Is", "Is Not")
+
+/datum/nanite_program/sensor/species/on_trigger(comm_message)
+ var/datum/nanite_extra_setting/species_type = extra_settings[NES_RACE]
+ var/species = allowed_species[species_type.get_value()]
+ var/species_match = FALSE
+
+ if(species)
+ if(is_species(host_mob, species))
+ species_match = TRUE
+ else //this is the check for the "Other" option
+ species_match = TRUE
+ for(var/name in allowed_species)
+ var/species_other = allowed_species[name]
+ if(is_species(host_mob, species_other))
+ species_match = FALSE
+ break
+
+ var/datum/nanite_extra_setting/mode = extra_settings[NES_MODE]
+ if(mode.get_value())
+ if(species_match)
+ send_code()
+ else
+ if(!species_match)
+ send_code()
diff --git a/massmeta/features/nanites/code/programs/suppression.dm b/massmeta/features/nanites/code/programs/suppression.dm
new file mode 100644
index 0000000000000..526074b3e6d24
--- /dev/null
+++ b/massmeta/features/nanites/code/programs/suppression.dm
@@ -0,0 +1,210 @@
+/datum/nanite_program/sleepy
+ name = "Sleep Induction"
+ desc = "The nanites cause rapid narcolepsy when triggered."
+ can_trigger = TRUE
+ trigger_cost = 15
+ trigger_cooldown = 1200
+ rogue_types = list(/datum/nanite_program/brain_misfire, /datum/nanite_program/brain_decay)
+
+/datum/nanite_program/sleepy/on_trigger(comm_message)
+ to_chat(host_mob, span_warning("You start to feel very sleepy..."))
+ host_mob.adjust_drowsiness(2 SECONDS)
+ addtimer(CALLBACK(host_mob, TYPE_PROC_REF(/mob/living, Sleeping), 200), rand(60,200))
+
+/datum/nanite_program/paralyzing
+ name = "Paralysis"
+ desc = "The nanites force muscle contraction, effectively paralyzing the host."
+ use_rate = 3
+ rogue_types = list(/datum/nanite_program/nerve_decay)
+
+/datum/nanite_program/paralyzing/active_effect()
+ host_mob.Stun(40)
+
+/datum/nanite_program/paralyzing/enable_passive_effect()
+ . = ..()
+ to_chat(host_mob, span_warning("Your muscles seize! You can't move!"))
+
+/datum/nanite_program/paralyzing/disable_passive_effect()
+ . = ..()
+ to_chat(host_mob, span_notice("Your muscles relax, and you can move again."))
+
+/datum/nanite_program/shocking
+ name = "Electric Shock"
+ desc = "The nanites shock the host when triggered. Destroys a large amount of nanites!"
+ can_trigger = TRUE
+ trigger_cost = 10
+ trigger_cooldown = 300
+ program_flags = NANITE_SHOCK_IMMUNE
+ rogue_types = list(/datum/nanite_program/toxic)
+
+/datum/nanite_program/shocking/on_trigger(comm_message)
+ host_mob.electrocute_act(rand(5,10), "shock nanites", 1, SHOCK_NOGLOVES)
+
+/datum/nanite_program/stun
+ name = "Neural Shock"
+ desc = "The nanites pulse the host's nerves when triggered, inapacitating them for a short period."
+ can_trigger = TRUE
+ trigger_cost = 4
+ trigger_cooldown = 300
+ rogue_types = list(/datum/nanite_program/shocking, /datum/nanite_program/nerve_decay)
+
+/datum/nanite_program/stun/on_trigger(comm_message)
+ playsound(host_mob, "sparks", 75, TRUE, -1, SHORT_RANGE_SOUND_EXTRARANGE)
+ host_mob.Paralyze(80)
+
+/datum/nanite_program/pacifying
+ name = "Pacification"
+ desc = "The nanites suppress the aggression center of the brain, preventing the host from causing direct harm to others."
+ use_rate = 1
+ rogue_types = list(/datum/nanite_program/brain_misfire, /datum/nanite_program/brain_decay)
+
+/datum/nanite_program/pacifying/enable_passive_effect()
+ . = ..()
+ ADD_TRAIT(host_mob, TRAIT_PACIFISM, TRAIT_NANITES)
+
+/datum/nanite_program/pacifying/disable_passive_effect()
+ . = ..()
+ REMOVE_TRAIT(host_mob, TRAIT_PACIFISM, TRAIT_NANITES)
+
+/datum/nanite_program/blinding
+ name = "Blindness"
+ desc = "The nanites suppress the host's ocular nerves, blinding them while they're active."
+ use_rate = 1.5
+ rogue_types = list(/datum/nanite_program/nerve_decay)
+
+/datum/nanite_program/blinding/enable_passive_effect()
+ . = ..()
+ host_mob.become_blind(TRAIT_NANITES)
+
+/datum/nanite_program/blinding/disable_passive_effect()
+ . = ..()
+ host_mob.cure_blind(TRAIT_NANITES)
+
+/datum/nanite_program/mute
+ name = "Mute"
+ desc = "The nanites suppress the host's speech, making them mute while they're active."
+ use_rate = 0.75
+ rogue_types = list(/datum/nanite_program/brain_decay, /datum/nanite_program/brain_misfire)
+
+/datum/nanite_program/mute/enable_passive_effect()
+ . = ..()
+ ADD_TRAIT(host_mob, TRAIT_MUTE, TRAIT_NANITES)
+
+/datum/nanite_program/mute/disable_passive_effect()
+ . = ..()
+ REMOVE_TRAIT(host_mob, TRAIT_MUTE, TRAIT_NANITES)
+
+/datum/nanite_program/fake_death
+ name = "Death Simulation"
+ desc = "The nanites induce a death-like coma into the host, able to fool most medical scans."
+ use_rate = 3.5
+ rogue_types = list(/datum/nanite_program/nerve_decay, /datum/nanite_program/necrotic, /datum/nanite_program/brain_decay)
+
+/datum/nanite_program/fake_death/enable_passive_effect()
+ . = ..()
+ host_mob.emote("deathgasp")
+ host_mob.fakedeath("nanites")
+
+/datum/nanite_program/fake_death/disable_passive_effect()
+ . = ..()
+ host_mob.cure_fakedeath("nanites")
+
+/datum/nanite_program/comm
+ can_trigger = TRUE
+ var/comm_message = ""
+
+/datum/nanite_program/comm/register_extra_settings()
+ extra_settings[NES_COMM_CODE] = new /datum/nanite_extra_setting/number(0, 0, 9999)
+
+/datum/nanite_program/comm/proc/receive_comm_signal(signal_comm_code, comm_message, comm_source)
+ var/datum/nanite_extra_setting/comm_code = extra_settings[NES_COMM_CODE]
+ if(!activated || !comm_code)
+ return
+ if(signal_comm_code == comm_code)
+ log_game("[host_mob]'s [name] nanite program was messaged by [comm_source] with comm code [signal_comm_code] and message '[comm_message]'.")
+ trigger(comm_message)
+
+/datum/nanite_program/comm/speech
+ name = "Forced Speech"
+ desc = "The nanites force the host to say a pre-programmed sentence when triggered."
+ unique = FALSE
+ trigger_cost = 3
+ trigger_cooldown = 20
+ rogue_types = list(/datum/nanite_program/brain_misfire, /datum/nanite_program/brain_decay)
+ var/static/list/blacklist = list(
+ "*surrender",
+ "*collapse",
+ )
+
+/datum/nanite_program/comm/speech/register_extra_settings()
+ . = ..()
+ extra_settings[NES_SENTENCE] = new /datum/nanite_extra_setting/text("")
+
+/datum/nanite_program/comm/speech/on_trigger(comm_message)
+ var/sent_message = comm_message
+ if(!comm_message)
+ var/datum/nanite_extra_setting/sentence = extra_settings[NES_SENTENCE]
+ sent_message = sentence.get_value()
+ if(sent_message in blacklist)
+ return
+ if(host_mob.stat == DEAD)
+ return
+ to_chat(host_mob, span_warning("You feel compelled to speak..."))
+ host_mob.say(sent_message, forced = "nanite speech")
+
+/datum/nanite_program/comm/voice
+ name = "Skull Echo"
+ desc = "The nanites echo a synthesized message inside the host's skull."
+ unique = FALSE
+ trigger_cost = 1
+ trigger_cooldown = 20
+ rogue_types = list(/datum/nanite_program/brain_misfire, /datum/nanite_program/brain_decay)
+
+/datum/nanite_program/comm/voice/register_extra_settings()
+ . = ..()
+ extra_settings[NES_MESSAGE] = new /datum/nanite_extra_setting/text("")
+
+/datum/nanite_program/comm/voice/on_trigger(comm_message)
+ var/sent_message = comm_message
+ if(!comm_message)
+ var/datum/nanite_extra_setting/message_setting = extra_settings[NES_MESSAGE]
+ sent_message = message_setting.get_value()
+ if(host_mob.stat == DEAD)
+ return
+ to_chat(host_mob, "You hear a strange, robotic voice in your head... \"[sent_message]\"")
+
+/datum/nanite_program/good_mood
+ name = "Happiness Enhancer"
+ desc = "The nanites synthesize serotonin inside the host's brain, creating an artificial sense of happiness."
+ use_rate = 0.1
+ rogue_types = list(/datum/nanite_program/brain_decay)
+
+/datum/nanite_program/good_mood/register_extra_settings()
+ . = ..()
+ extra_settings[NES_MOOD_MESSAGE] = new /datum/nanite_extra_setting/text("HAPPINESS ENHANCEMENT")
+
+/datum/nanite_program/good_mood/enable_passive_effect()
+ . = ..()
+ host_mob.add_mood_event("nanite_happy", /datum/mood_event/nanite_happiness, get_extra_setting_value(NES_MOOD_MESSAGE))
+
+/datum/nanite_program/good_mood/disable_passive_effect()
+ . = ..()
+ host_mob.clear_mood_event("nanite_happy")
+
+/datum/nanite_program/bad_mood
+ name = "Happiness Suppressor"
+ desc = "The nanites suppress the production of serotonin inside the host's brain, creating an artificial state of depression."
+ use_rate = 0.1
+ rogue_types = list(/datum/nanite_program/brain_decay)
+
+/datum/nanite_program/bad_mood/register_extra_settings()
+ . = ..()
+ extra_settings[NES_MOOD_MESSAGE] = new /datum/nanite_extra_setting/text("HAPPINESS SUPPRESSION")
+
+/datum/nanite_program/bad_mood/enable_passive_effect()
+ . = ..()
+ host_mob.add_mood_event("nanite_sadness", /datum/mood_event/nanite_sadness, get_extra_setting_value(NES_MOOD_MESSAGE))
+
+/datum/nanite_program/bad_mood/disable_passive_effect()
+ . = ..()
+ host_mob.clear_mood_event("nanite_sadness")
diff --git a/massmeta/features/nanites/code/programs/utility.dm b/massmeta/features/nanites/code/programs/utility.dm
new file mode 100644
index 0000000000000..564fbc7aa6a98
--- /dev/null
+++ b/massmeta/features/nanites/code/programs/utility.dm
@@ -0,0 +1,330 @@
+//Programs that interact with other programs or nanites directly, or have other special purposes.
+/datum/nanite_program/viral
+ name = "Viral Replica"
+ desc = "The nanites constantly send encrypted signals attempting to forcefully copy their own programming into other nanite clusters, also overriding or disabling their cloud sync."
+ use_rate = 0.5
+ rogue_types = list(/datum/nanite_program/toxic)
+
+ ///The cooldown between pulses.
+ COOLDOWN_DECLARE(pulse_cooldown)
+
+/datum/nanite_program/viral/register_extra_settings()
+ extra_settings[NES_PROGRAM_OVERWRITE] = new /datum/nanite_extra_setting/type("Add To", list("Overwrite", "Add To", "Ignore"))
+ extra_settings[NES_CLOUD_OVERWRITE] = new /datum/nanite_extra_setting/number(0, 0, 100)
+
+/datum/nanite_program/viral/active_effect()
+ if(!COOLDOWN_FINISHED(src, pulse_cooldown))
+ return
+ 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/living/people_in_range in orange(host_mob, 5))
+ if(SEND_SIGNAL(people_in_range, COMSIG_NANITE_IS_STEALTHY))
+ continue
+ switch(program.get_value())
+ if("Overwrite")
+ SEND_SIGNAL(people_in_range, COMSIG_NANITE_SYNC, nanites, TRUE)
+ if("Add To")
+ SEND_SIGNAL(people_in_range, COMSIG_NANITE_SYNC, nanites, FALSE)
+ SEND_SIGNAL(people_in_range, COMSIG_NANITE_SET_CLOUD, cloud.get_value())
+ COOLDOWN_START(src, pulse_cooldown, 7.5 SECONDS)
+
+/datum/nanite_program/self_scan
+ name = "Host Scan"
+ desc = "The nanites display a detailed readout of a body scan to the host."
+ unique = FALSE
+ can_trigger = TRUE
+ trigger_cost = 3
+ trigger_cooldown = 50
+ rogue_types = list(/datum/nanite_program/toxic)
+
+/datum/nanite_program/self_scan/register_extra_settings()
+ extra_settings[NES_SCAN_TYPE] = new /datum/nanite_extra_setting/type("Medical", list("Medical", "Chemical", "Nanite"))
+
+/datum/nanite_program/self_scan/on_trigger(comm_message)
+ if(host_mob.stat == DEAD)
+ return
+
+ var/datum/nanite_extra_setting/scanned_nanites = extra_settings[NES_SCAN_TYPE]
+ switch(scanned_nanites.get_value())
+ if("Medical")
+ healthscan(host_mob, host_mob)
+ if("Chemical")
+ chemscan(host_mob, host_mob)
+ if("Nanite")
+ SEND_SIGNAL(host_mob, COMSIG_NANITE_SCAN, host_mob, TRUE)
+
+/datum/nanite_program/stealth
+ name = "Stealth"
+ desc = "The nanites mask their activity from superficial scans, becoming undetectable by HUDs and non-specialized scanners."
+ rogue_types = list(/datum/nanite_program/toxic)
+ use_rate = 0.2
+
+/datum/nanite_program/stealth/enable_passive_effect()
+ . = ..()
+ nanites.stealth = TRUE
+ host_mob.hud_set_nanite_indicator(remove = TRUE)
+
+/datum/nanite_program/stealth/disable_passive_effect()
+ . = ..()
+ nanites.stealth = FALSE
+ host_mob.hud_set_nanite_indicator()
+
+/datum/nanite_program/reduced_diagnostics
+ name = "Reduced Diagnostics"
+ desc = "Disables some high-cost diagnostics in the nanites, making them unable to communicate their program list to portable scanners. \
+ Doing so saves some power, slightly increasing their replication speed."
+ rogue_types = list(/datum/nanite_program/toxic)
+ use_rate = -0.1
+
+/datum/nanite_program/reduced_diagnostics/enable_passive_effect()
+ . = ..()
+ nanites.diagnostics = FALSE
+
+/datum/nanite_program/reduced_diagnostics/disable_passive_effect()
+ . = ..()
+ nanites.diagnostics = TRUE
+
+/datum/nanite_program/relay
+ name = "Relay"
+ desc = "The nanites receive and relay long-range nanite signals."
+ rogue_types = list(/datum/nanite_program/toxic)
+
+/datum/nanite_program/relay/register_extra_settings()
+ extra_settings[NES_RELAY_CHANNEL] = new /datum/nanite_extra_setting/number(1, 1, 9999)
+
+/datum/nanite_program/relay/enable_passive_effect()
+ . = ..()
+ SSnanites.nanite_relays |= src
+
+/datum/nanite_program/relay/disable_passive_effect()
+ . = ..()
+ SSnanites.nanite_relays -= src
+
+/datum/nanite_program/relay/proc/relay_signal(code, relay_code, source)
+ if(!activated)
+ return
+ if(!host_mob)
+ return
+ var/datum/nanite_extra_setting/nanite_setting = extra_settings[NES_RELAY_CHANNEL]
+ if(relay_code != nanite_setting.get_value())
+ return
+ SEND_SIGNAL(host_mob, COMSIG_NANITE_SIGNAL, code, source)
+
+/datum/nanite_program/relay/proc/relay_comm_signal(comm_code, relay_code, comm_message)
+ if(!activated)
+ return
+ if(!host_mob)
+ return
+ var/datum/nanite_extra_setting/nanite_setting = extra_settings[NES_RELAY_CHANNEL]
+ if(relay_code != nanite_setting.get_value())
+ return
+ SEND_SIGNAL(host_mob, COMSIG_NANITE_COMM_SIGNAL, comm_code, comm_message)
+
+/datum/nanite_program/metabolic_synthesis
+ name = "Metabolic Synthesis"
+ desc = "The nanites use the metabolic cycle of the host to speed up their replication rate, using their extra nutrition as fuel."
+ use_rate = -0.5 //generates nanites
+ rogue_types = list(/datum/nanite_program/toxic)
+
+/datum/nanite_program/metabolic_synthesis/check_conditions()
+ if(!iscarbon(host_mob))
+ return FALSE
+ var/mob/living/carbon/C = host_mob
+ if(C.nutrition <= NUTRITION_LEVEL_WELL_FED)
+ return FALSE
+ return ..()
+
+/datum/nanite_program/metabolic_synthesis/active_effect()
+ host_mob.adjust_nutrition(-0.5)
+
+/datum/nanite_program/access
+ name = "Subdermal ID"
+ desc = "The nanites store the host's ID access rights in a subdermal magnetic strip. Updates when triggered, copying the host's current access."
+ can_trigger = TRUE
+ trigger_cost = 3
+ trigger_cooldown = 30
+ rogue_types = list(/datum/nanite_program/skin_decay)
+ ///List of all access that the Subdermal ID is currently holding onto.
+ var/list/access = list()
+
+//Syncs the nanites with the cumulative current mob's access level. Can potentially wipe existing access.
+/datum/nanite_program/access/on_trigger(comm_message)
+ var/list/new_access = list()
+ var/obj/item/current_item
+ current_item = host_mob.get_active_held_item()
+ if(current_item)
+ new_access += current_item.GetAccess()
+ current_item = host_mob.get_inactive_held_item()
+ if(current_item)
+ new_access += current_item.GetAccess()
+ if(ishuman(host_mob))
+ var/mob/living/carbon/human/human_host = host_mob
+ current_item = human_host.wear_id
+ if(current_item)
+ new_access += current_item.GetAccess()
+ else if(isanimal(host_mob))
+ var/mob/living/simple_animal/animal_host = host_mob
+ current_item = animal_host.access_card
+ if(current_item)
+ new_access += current_item.GetAccess()
+ access = new_access
+
+/datum/nanite_program/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."
+ use_rate = 1.50
+ rogue_types = list(/datum/nanite_program/aggressive_replication, /datum/nanite_program/necrotic)
+ COOLDOWN_DECLARE(spread_delay)
+
+/datum/nanite_program/spreading/active_effect()
+ if(!COOLDOWN_FINISHED(src, spread_delay))
+ return
+ COOLDOWN_START(src, spread_delay, 8 SECONDS)
+
+ var/list/mob/living/carbon/human/target_hosts = list()
+ for(var/mob/living/carbon/human/nearby_humans in oview(5, host_mob))
+ if(!prob(25))
+ continue
+ if(!(nearby_humans.mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD)))
+ continue
+ target_hosts += nearby_humans
+ if(!target_hosts.len)
+ return
+ var/mob/living/carbon/human/infectee = pick(target_hosts)
+ if(!(infectee.wear_suit) || prob(100 - infectee.wear_suit.get_armor_rating(BIO)))
+ //this will potentially take over existing nanites!
+ infectee.AddComponent(/datum/component/nanites, null, 10)
+ SEND_SIGNAL(infectee, COMSIG_NANITE_SYNC, nanites)
+ log_game("[infectee] was infected by spreading nanites by [key_name(host_mob)] at [AREACOORD(infectee)].")
+
+/datum/nanite_program/nanite_sting
+ name = "Nanite Sting"
+ desc = "When triggered, projects a nearly invisible spike of nanites that attempts to infect a nearby non-host with a copy of the host's nanites cluster."
+ can_trigger = TRUE
+ trigger_cost = 5
+ trigger_cooldown = 100
+ rogue_types = list(/datum/nanite_program/glitch, /datum/nanite_program/toxic)
+
+/datum/nanite_program/nanite_sting/on_trigger(comm_message)
+ var/list/mob/living/carbon/human/target_hosts = list()
+ for(var/mob/living/carbon/human/nearby_humans in oview(1, host_mob))
+ var/datum/component/nanites/nanites = nearby_humans.GetComponent(/datum/component/nanites)
+ if(!(nearby_humans.mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD)) || nanites || !nearby_humans.Adjacent(host_mob))
+ continue
+ target_hosts += nearby_humans
+ if(!target_hosts.len)
+ consume_nanites(-5)
+ return
+ var/mob/living/carbon/human/infectee = pick(target_hosts)
+ if(!(infectee.wear_suit) || prob(100 - infectee.wear_suit.get_armor_rating(BIO)))
+ //unlike with Infective Exo-Locomotion, this can't take over existing nanites, because Nanite Sting only targets non-hosts.
+ infectee.AddComponent(/datum/component/nanites, null, 5)
+ SEND_SIGNAL(infectee, COMSIG_NANITE_SYNC, nanites)
+ log_game("[infectee] was infected by a nanite cluster by [key_name(host_mob)] at [AREACOORD(infectee)].")
+ to_chat(infectee, span_warning("You feel a tiny prick."))
+
+/datum/nanite_program/mitosis
+ name = "Mitosis"
+ desc = "The nanites gain the ability to self-replicate, using bluespace to power the process. Becomes more effective the more nanites are already in the host.\
+ The replication has also a chance to corrupt the nanite programming due to copy faults - cloud sync is highly recommended."
+ use_rate = 0
+ rogue_types = list(/datum/nanite_program/toxic)
+
+/datum/nanite_program/mitosis/active_effect()
+ var/rep_rate = round(nanites.nanite_volume / 50, 1) //0.5 per 50 nanite volume
+ rep_rate *= 0.5
+ nanites.adjust_nanites(null, rep_rate)
+ if(prob(rep_rate))
+ var/datum/nanite_program/fault = pick(nanites.programs)
+ if(fault == src)
+ return
+ fault.software_error()
+
+/datum/nanite_program/dermal_button
+ name = "Dermal Button"
+ desc = "Displays a button on the host's skin, which can be used to send a signal to the nanites."
+ unique = FALSE
+ var/datum/action/innate/nanite_button/button
+
+/datum/nanite_program/dermal_button/register_extra_settings()
+ extra_settings[NES_SENT_CODE] = new /datum/nanite_extra_setting/number(1, 1, 9999)
+ extra_settings[NES_BUTTON_NAME] = new /datum/nanite_extra_setting/text("Button")
+ extra_settings[NES_ICON] = new /datum/nanite_extra_setting/type("power", list(
+ "blank",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "plus",
+ "minus",
+ "exclamation",
+ "question",
+ "cross",
+ "info",
+ "heart",
+ "skull",
+ "brain",
+ "brain_damage",
+ "injection",
+ "blood",
+ "shield",
+ "reaction",
+ "network",
+ "power",
+ "radioactive",
+ "electricity",
+ "magnetism",
+ "scan",
+ "repair",
+ "id",
+ "wireless",
+ "say",
+ "sleep",
+ "bomb",
+ ))
+
+/datum/nanite_program/dermal_button/enable_passive_effect()
+ . = ..()
+ var/datum/nanite_extra_setting/bn_name = extra_settings[NES_BUTTON_NAME]
+ var/datum/nanite_extra_setting/bn_icon = extra_settings[NES_ICON]
+ if(!button)
+ button = new(src, bn_name.get_value(), bn_icon.get_value())
+ button.target = host_mob
+ button.Grant(host_mob)
+
+/datum/nanite_program/dermal_button/disable_passive_effect()
+ . = ..()
+ if(button)
+ button.Remove(host_mob)
+
+/datum/nanite_program/dermal_button/on_mob_remove()
+ qdel(button)
+ return ..()
+
+/datum/nanite_program/dermal_button/proc/press()
+ if(activated)
+ host_mob.visible_message(
+ span_notice("[host_mob] presses a button on [host_mob.p_their()] forearm."),
+ span_notice("You press the nanite button on your forearm."),
+ null, 2,
+ )
+ var/datum/nanite_extra_setting/sent_code = extra_settings[NES_SENT_CODE]
+ SEND_SIGNAL(host_mob, COMSIG_NANITE_SIGNAL, sent_code.get_value(), "a [name] program")
+
+/datum/action/innate/nanite_button
+ name = "Button"
+ button_icon = 'massmeta/features/nanites/icons/nanite_actions.dmi'
+ button_icon_state = "power_green"
+ check_flags = AB_CHECK_HANDS_BLOCKED|AB_CHECK_IMMOBILE|AB_CHECK_CONSCIOUS
+ var/datum/nanite_program/dermal_button/program
+
+/datum/action/innate/nanite_button/New(datum/nanite_program/dermal_button/program, program_name, program_icon)
+ ..()
+ src.program = program
+ src.name = program_name
+ button_icon_state = "nanite_[program_icon]"
+
+/datum/action/innate/nanite_button/Activate()
+ program.press()
diff --git a/massmeta/features/nanites/code/programs/weapon.dm b/massmeta/features/nanites/code/programs/weapon.dm
new file mode 100644
index 0000000000000..718a3cd185382
--- /dev/null
+++ b/massmeta/features/nanites/code/programs/weapon.dm
@@ -0,0 +1,188 @@
+//Programs specifically engineered to cause harm to either the user or its surroundings (as opposed to ones that only do it due to broken programming)
+//Very dangerous!
+
+/datum/nanite_program/flesh_eating
+ name = "Cellular Breakdown"
+ desc = "The nanites destroy cellular structures in the host's body, causing brute damage."
+ use_rate = 1.5
+ rogue_types = list(/datum/nanite_program/necrotic)
+
+/datum/nanite_program/flesh_eating/active_effect()
+ if(iscarbon(host_mob))
+ var/mob/living/carbon/carbon_host = host_mob
+ carbon_host.take_bodypart_damage(1, 0, 0)
+ else
+ host_mob.adjustBruteLoss(1, TRUE)
+ if(prob(3))
+ to_chat(host_mob, span_warning("You feel a stab of pain from somewhere inside you."))
+
+/datum/nanite_program/poison
+ name = "Poisoning"
+ desc = "The nanites deliver poisonous chemicals to the host's internal organs, causing toxin damage and vomiting."
+ use_rate = 1.5
+ rogue_types = list(/datum/nanite_program/toxic)
+
+/datum/nanite_program/poison/active_effect()
+ host_mob.adjustToxLoss(1)
+ if(prob(2))
+ to_chat(host_mob, span_warning("You feel nauseous."))
+ if(iscarbon(host_mob))
+ var/mob/living/carbon/C = host_mob
+ C.vomit(vomit_type = /obj/effect/decal/cleanable/vomit/nanites)
+
+/datum/nanite_program/memory_leak
+ name = "Memory Leak"
+ desc = "This program invades the memory space used by other programs, causing frequent corruptions and errors."
+ use_rate = 0
+ rogue_types = list(/datum/nanite_program/toxic)
+
+/datum/nanite_program/memory_leak/active_effect()
+ if(prob(6))
+ var/datum/nanite_program/target = pick(nanites.programs)
+ if(target == src)
+ return
+ target.software_error()
+
+/datum/nanite_program/aggressive_replication
+ name = "Aggressive Replication"
+ desc = "Nanites will consume organic matter to improve their replication rate, damaging the host. The efficiency increases with the volume of nanites, requiring 200 to break even."
+ use_rate = 1
+ rogue_types = list(/datum/nanite_program/necrotic)
+
+/datum/nanite_program/aggressive_replication/active_effect()
+ var/extra_regen = round(nanites.nanite_volume / 200, 0.1)
+ nanites.adjust_nanites(null, extra_regen)
+ host_mob.adjustBruteLoss(extra_regen / 2, TRUE)
+
+/datum/nanite_program/meltdown
+ name = "Meltdown"
+ desc = "Causes an internal meltdown inside the nanites, causing internal burns inside the host as well as rapidly destroying the nanite population.\
+ Sets the nanites' safety threshold to 0 when activated."
+ use_rate = 10
+ rogue_types = list(/datum/nanite_program/glitch)
+
+/datum/nanite_program/meltdown/active_effect()
+ host_mob.adjustFireLoss(3.5)
+
+/datum/nanite_program/meltdown/enable_passive_effect()
+ . = ..()
+ to_chat(host_mob, span_userdanger("Your blood is burning!"))
+ nanites.safety_threshold = 0
+
+/datum/nanite_program/meltdown/disable_passive_effect()
+ . = ..()
+ to_chat(host_mob, span_warning("Your blood cools down, and the pain gradually fades."))
+
+/datum/nanite_program/explosive
+ name = "Chain Detonation"
+ desc = "Detonates all the nanites inside the host in a chain reaction when triggered."
+ can_trigger = TRUE
+ trigger_cost = 25 //plus every idle nanite left afterwards
+ trigger_cooldown = 100 //Just to avoid double-triggering
+ rogue_types = list(/datum/nanite_program/toxic)
+
+/datum/nanite_program/explosive/on_trigger(comm_message)
+ host_mob.visible_message(
+ span_warning("[host_mob] starts emitting a high-pitched buzzing, and [host_mob.p_their()] skin begins to glow..."),
+ span_userdanger("You start emitting a high-pitched buzzing, and your skin begins to glow..."),
+ )
+ addtimer(CALLBACK(src, PROC_REF(boom)), clamp((nanites.nanite_volume * 0.35), 25, 150))
+
+/datum/nanite_program/explosive/proc/boom()
+ var/nanite_amount = nanites.nanite_volume
+ var/dev_range = FLOOR(nanite_amount/200, 1) - 1
+ var/heavy_range = FLOOR(nanite_amount/100, 1) - 1
+ var/light_range = FLOOR(nanite_amount/50, 1) - 1
+ explosion(host_mob, dev_range, heavy_range, light_range)
+ qdel(nanites)
+
+/datum/nanite_program/heart_stop
+ name = "Heart-Stopper"
+ desc = "Stops the host's heart when triggered; restarts it if triggered again."
+ can_trigger = TRUE
+ trigger_cost = 12
+ trigger_cooldown = 10
+ rogue_types = list(/datum/nanite_program/nerve_decay)
+
+/datum/nanite_program/heart_stop/on_trigger(comm_message)
+ if(!iscarbon(host_mob))
+ return
+ var/mob/living/carbon/C = host_mob
+ var/obj/item/organ/internal/heart/heart = C.get_organ_slot(ORGAN_SLOT_HEART)
+ if(heart)
+ if(heart.is_beating())
+ heart.Stop()
+ else
+ heart.Restart()
+
+/datum/nanite_program/emp
+ name = "Electromagnetic Resonance"
+ desc = "The nanites cause an electromagnetic pulse around the host when triggered. Will corrupt other nanite programs!"
+ can_trigger = TRUE
+ trigger_cost = 10
+ trigger_cooldown = 50
+ program_flags = NANITE_EMP_IMMUNE
+ rogue_types = list(/datum/nanite_program/toxic)
+
+/datum/nanite_program/emp/on_trigger(comm_message)
+ empulse(host_mob, 1, 2)
+
+/datum/nanite_program/pyro
+ name = "Sub-Dermal Combustion"
+ desc = "The nanites cause buildup of flammable fluids under the host's skin, then ignites them."
+ use_rate = 4
+ rogue_types = list(/datum/nanite_program/skin_decay, /datum/nanite_program/cryo)
+
+/datum/nanite_program/pyro/check_conditions()
+ if(host_mob.fire_stacks >= 10 && host_mob.on_fire)
+ return FALSE
+ return ..()
+
+/datum/nanite_program/pyro/active_effect()
+ host_mob.fire_stacks += 1
+ host_mob.ignite_mob()
+
+/datum/nanite_program/cryo
+ name = "Cryogenic Treatment"
+ desc = "The nanites rapidly sink heat through the host's skin, lowering their temperature."
+ use_rate = 1
+ rogue_types = list(/datum/nanite_program/skin_decay, /datum/nanite_program/pyro)
+
+/datum/nanite_program/cryo/check_conditions()
+ if(host_mob.bodytemperature <= 70)
+ return FALSE
+ return ..()
+
+/datum/nanite_program/cryo/active_effect()
+ host_mob.adjust_bodytemperature(-rand(15,25), 50)
+
+/datum/nanite_program/comm/mind_control
+ name = "Mind Control"
+ desc = "The nanites imprint an absolute directive onto the host's brain for one minute when triggered."
+ trigger_cost = 30
+ trigger_cooldown = 1800
+ rogue_types = list(/datum/nanite_program/brain_decay, /datum/nanite_program/brain_misfire)
+
+/datum/nanite_program/comm/mind_control/register_extra_settings()
+ . = ..()
+ extra_settings[NES_DIRECTIVE] = new /datum/nanite_extra_setting/text("...")
+
+/datum/nanite_program/comm/mind_control/on_trigger(comm_message)
+ if(host_mob.stat == DEAD)
+ return
+ var/sent_directive = comm_message
+ if(!comm_message)
+ var/datum/nanite_extra_setting/ES = extra_settings[NES_DIRECTIVE]
+ sent_directive = ES.get_value()
+ brainwash(host_mob, sent_directive)
+ log_game("A mind control nanite program brainwashed [key_name(host_mob)] with the objective '[sent_directive]'.")
+ addtimer(CALLBACK(src, PROC_REF(end_brainwashing)), 60 SECONDS)
+
+/datum/nanite_program/comm/mind_control/proc/end_brainwashing()
+ if(host_mob.mind && host_mob.mind.has_antag_datum(/datum/antagonist/brainwashed))
+ host_mob.mind.remove_antag_datum(/datum/antagonist/brainwashed)
+ log_game("[key_name(host_mob)] is no longer brainwashed by nanites.")
+
+/datum/nanite_program/comm/mind_control/disable_passive_effect()
+ . = ..()
+ end_brainwashing()
diff --git a/massmeta/features/nanites/code/research.dm b/massmeta/features/nanites/code/research.dm
new file mode 100644
index 0000000000000..ad398134a0d49
--- /dev/null
+++ b/massmeta/features/nanites/code/research.dm
@@ -0,0 +1,232 @@
+/datum/techweb_node/nanite_base
+ id = "nanite_base"
+ display_name = "Basic Nanite Programming"
+ description = "The basics of nanite construction and programming."
+ prereq_ids = list("datatheory")
+ design_ids = list(
+ "nanite_disk",
+ "nanite_remote",
+ "nanite_comm_remote",
+ "nanite_scanner",
+ "nanite_chamber",
+ "nanite_chamber_control",
+ "nanite_programmer",
+ "nanite_program_hub",
+ "nanite_cloud_control",
+ "relay_nanites",
+ "access_nanites",
+ "repairing_nanites",
+ "sensor_nanite_volume",
+ "repeater_nanites",
+ "relay_repeater_nanites",
+ "red_diag_nanites",
+ )
+ research_costs = list(
+ TECHWEB_POINT_TYPE_GENERIC = 1000,
+ )
+
+/datum/techweb_node/nanite_smart
+ id = "nanite_smart"
+ display_name = "Smart Nanite Programming"
+ description = "Nanite programs that require nanites to perform complex actions, act independently, roam or seek targets."
+ prereq_ids = list(
+ "nanite_base",
+ "robotics",
+ )
+ design_ids = list(
+ "purging_nanites",
+ "metabolic_nanites",
+ "stealth_nanites",
+ "memleak_nanites",
+ "sensor_voice_nanites",
+ "voice_nanites",
+ )
+ research_costs = list(
+ TECHWEB_POINT_TYPE_GENERIC = 750,
+ TECHWEB_POINT_TYPE_NANITES = 500,
+ )
+
+/datum/techweb_node/nanite_mesh
+ id = "nanite_mesh"
+ 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",
+ )
+ research_costs = list(
+ TECHWEB_POINT_TYPE_GENERIC = 750,
+ TECHWEB_POINT_TYPE_NANITES = 500,
+ )
+
+/datum/techweb_node/nanite_bio
+ id = "nanite_bio"
+ display_name = "Biological Nanite Programming"
+ description = "Nanite programs that require complex biological interaction."
+ prereq_ids = list(
+ "nanite_base",
+ "biotech",
+ )
+ design_ids = list(
+ "regenerative_nanites",
+ "bloodheal_nanites",
+ "coagulating_nanites",
+ "poison_nanites",
+ "flesheating_nanites",
+ "sensor_crit_nanites",
+ "sensor_death_nanites",
+ "sensor_health_nanites",
+ "sensor_damage_nanites",
+ "sensor_species_nanites",
+ )
+ research_costs = list(
+ TECHWEB_POINT_TYPE_GENERIC = 750,
+ TECHWEB_POINT_TYPE_NANITES = 500,
+ )
+
+/datum/techweb_node/nanite_neural
+ id = "nanite_neural"
+ display_name = "Neural Nanite Programming"
+ description = "Nanite programs affecting nerves and brain matter."
+ prereq_ids = list("nanite_bio")
+ design_ids = list(
+ "nervous_nanites",
+ "brainheal_nanites",
+ "paralyzing_nanites",
+ "stun_nanites",
+ "selfscan_nanites",
+ "good_mood_nanites",
+ "bad_mood_nanites",
+ )
+ research_costs = list(
+ TECHWEB_POINT_TYPE_GENERIC = 1500,
+ TECHWEB_POINT_TYPE_NANITES = 1000,
+ )
+
+/datum/techweb_node/nanite_synaptic
+ id = "nanite_synaptic"
+ display_name = "Synaptic Nanite Programming"
+ description = "Nanite programs affecting mind and thoughts."
+ prereq_ids = list(
+ "nanite_neural",
+ "neural_programming",
+ )
+ design_ids = list(
+ "mindshield_nanites",
+ "pacifying_nanites",
+ "blinding_nanites",
+ "sleep_nanites",
+ "mute_nanites",
+ "speech_nanites",
+ )
+ research_costs = list(
+ TECHWEB_POINT_TYPE_GENERIC = 1500,
+ TECHWEB_POINT_TYPE_NANITES = 1000,
+ )
+
+/datum/techweb_node/nanite_harmonic
+ id = "nanite_harmonic"
+ display_name = "Harmonic Nanite Programming"
+ description = "Nanite programs that require seamless integration between nanites and biology."
+ prereq_ids = list(
+ "nanite_bio",
+ "nanite_smart",
+ "nanite_mesh",
+ )
+ design_ids = list(
+ "fakedeath_nanites",
+ "aggressive_nanites",
+ "defib_nanites",
+ "regenerative_plus_nanites",
+ "brainheal_plus_nanites",
+ "purging_plus_nanites",
+ "adrenaline_nanites",
+ )
+ research_costs = list(
+ TECHWEB_POINT_TYPE_GENERIC = 2750,
+ TECHWEB_POINT_TYPE_NANITES = 3000,
+ )
+
+/datum/techweb_node/nanite_combat
+ id = "nanite_military"
+ display_name = "Military Nanite Programming"
+ description = "Nanite programs that perform military-grade functions."
+ prereq_ids = list(
+ "nanite_harmonic",
+ "syndicate_basic",
+ )
+ design_ids = list(
+ "explosive_nanites",
+ "pyro_nanites",
+ "meltdown_nanites",
+ "viral_nanites",
+ "nanite_sting_nanites",
+ )
+ research_costs = list(
+ TECHWEB_POINT_TYPE_GENERIC = 3500,
+ TECHWEB_POINT_TYPE_NANITES = 2500,
+ )
+
+/datum/techweb_node/nanite_hazard
+ id = "nanite_hazard"
+ display_name = "Hazard Nanite Programs"
+ description = "Extremely advanced Nanite programs with the potential of being extremely dangerous."
+ prereq_ids = list(
+ "nanite_harmonic",
+ "alientech",
+ )
+ design_ids = list(
+ "spreading_nanites",
+ "mindcontrol_nanites",
+ "mitosis_nanites",
+ )
+ research_costs = list(
+ TECHWEB_POINT_TYPE_GENERIC = 3500,
+ TECHWEB_POINT_TYPE_NANITES = 4000,
+ )
+
+/datum/techweb_node/nanite_replication_protocols
+ id = "nanite_replication_protocols"
+ display_name = "Nanite Replication Protocols"
+ description = "Advanced behaviours that allow nanites to exploit certain circumstances to replicate faster."
+ prereq_ids = list("nanite_smart")
+ design_ids = list(
+ "kickstart_nanites",
+ "factory_nanites",
+ "tinker_nanites",
+ "offline_nanites",
+ )
+ research_costs = list(
+ TECHWEB_POINT_TYPE_GENERIC = 2000,
+ TECHWEB_POINT_TYPE_NANITES = 2500,
+ )
+ hidden = TRUE
+ experimental = TRUE
+
+/datum/techweb_node/nanite_storage_protocols
+ id = "nanite_storage_protocols"
+ display_name = "Nanite Storage Protocols"
+ description = "Protocols that overwrite the default nanite storage routine to achieve more efficiency or greater capacity."
+ prereq_ids = list("nanite_smart")
+ design_ids = list(
+ "free_range_nanites",
+ "hive_nanites",
+ "unsafe_storage_nanites",
+ "zip_nanites",
+ )
+ research_costs = list(
+ TECHWEB_POINT_TYPE_GENERIC = 1000,
+ TECHWEB_POINT_TYPE_NANITES = 2500,
+ )
+ hidden = TRUE
+ experimental = TRUE
diff --git a/massmeta/features/nanites/code/settings/extra_settings.dm b/massmeta/features/nanites/code/settings/extra_settings.dm
new file mode 100644
index 0000000000000..5a35078f70e68
--- /dev/null
+++ b/massmeta/features/nanites/code/settings/extra_settings.dm
@@ -0,0 +1,87 @@
+/datum/nanite_extra_setting
+ var/setting_type
+ var/value
+
+/datum/nanite_extra_setting/proc/get_value()
+ return value
+
+/datum/nanite_extra_setting/proc/set_value(value)
+ src.value = value
+
+/datum/nanite_extra_setting/proc/get_copy()
+ return
+
+//I made the choice to send the name as part of the parameter instead of storing it directly on
+//this datum as a way of avoiding duplication of data between the containing assoc list
+//and this datum.
+//Also make sure to double wrap the list when implementing this as
+//+= is interpreted as a combine on lists, so the outer list gets unwrapped
+/datum/nanite_extra_setting/proc/get_frontend_list(name)
+ return
+
+/**
+ * Boolean
+ */
+/datum/nanite_extra_setting/boolean
+ setting_type = NESTYPE_BOOLEAN
+ var/true_text
+ var/false_text
+
+/datum/nanite_extra_setting/boolean/New(initial, true_text, false_text)
+ value = initial
+ src.true_text = true_text
+ src.false_text = false_text
+
+/datum/nanite_extra_setting/boolean/set_value(value)
+ if(isnull(value))
+ src.value = !src.value
+ return
+ . = ..()
+
+/datum/nanite_extra_setting/boolean/get_copy()
+ return new /datum/nanite_extra_setting/boolean(value, true_text, false_text)
+
+/datum/nanite_extra_setting/boolean/get_frontend_list(name)
+ return list(list(
+ "name" = name,
+ "type" = setting_type,
+ "value" = value,
+ "true_text" = true_text,
+ "false_text" = false_text,
+ ))
+
+/**
+ * Number
+ */
+/datum/nanite_extra_setting/number
+ setting_type = NESTYPE_NUMBER
+ var/min
+ var/max
+ var/unit = ""
+
+/datum/nanite_extra_setting/number/New(initial, min, max, unit)
+ value = initial
+ src.min = min
+ src.max = max
+ if(unit)
+ src.unit = unit
+
+/datum/nanite_extra_setting/number/set_value(value)
+ if(istext(value))
+ value = text2num(value)
+ if(!value || !isnum(value))
+ return
+ src.value = clamp(value, min, max)
+
+/datum/nanite_extra_setting/number/get_copy()
+ return new /datum/nanite_extra_setting/number(value, min, max, unit)
+
+/datum/nanite_extra_setting/number/get_frontend_list(name)
+ return list(list(
+ "name" = name,
+ "type" = setting_type,
+ "value" = value,
+ "min" = min,
+ "max" = max,
+ "unit" = unit,
+ ))
diff --git a/massmeta/features/nanites/code/settings/rules.dm b/massmeta/features/nanites/code/settings/rules.dm
new file mode 100644
index 0000000000000..9a6d3ffc5d2f9
--- /dev/null
+++ b/massmeta/features/nanites/code/settings/rules.dm
@@ -0,0 +1,130 @@
+/datum/nanite_rule
+ var/name = "Generic Condition"
+ var/desc = "When triggered, the program is active"
+ var/datum/nanite_program/program
+
+/datum/nanite_rule/New(datum/nanite_program/new_program)
+ program = new_program
+ if(LAZYLEN(new_program.rules) <= 5) //Avoid infinite stacking rules
+ new_program.rules += src
+ else
+ qdel(src)
+
+/datum/nanite_rule/proc/remove()
+ program.rules -= src
+ program = null
+ qdel(src)
+
+/datum/nanite_rule/proc/check_rule()
+ return TRUE
+
+/datum/nanite_rule/proc/display()
+ return name
+
+/datum/nanite_rule/proc/copy_to(datum/nanite_program/new_program)
+ new type(new_program)
+
+/datum/nanite_rule/health
+ name = "Health"
+ desc = "Checks the host's health status."
+
+ var/threshold = 50
+ var/above = TRUE
+
+/datum/nanite_rule/health/check_rule()
+ var/health_percent = program.host_mob.health / program.host_mob.maxHealth * 100
+ if(above)
+ if(health_percent >= threshold)
+ return TRUE
+ else
+ if(health_percent < threshold)
+ return TRUE
+ return FALSE
+
+/datum/nanite_rule/health/display()
+ return "[name] [above ? ">" : "<"] [threshold]%"
+
+/datum/nanite_rule/health/copy_to(datum/nanite_program/new_program)
+ var/datum/nanite_rule/health/rule = new(new_program)
+ rule.above = above
+ rule.threshold = threshold
+
+//TODO allow inversion
+/datum/nanite_rule/crit
+ name = "Crit"
+ desc = "Checks if the host is in critical condition."
+
+/datum/nanite_rule/crit/check_rule()
+ return HAS_TRAIT(program.host_mob, TRAIT_CRITICAL_CONDITION)
+
+
+/datum/nanite_rule/death
+ name = "Death"
+ desc = "Checks if the host is dead."
+
+/datum/nanite_rule/death/check_rule()
+ if(program.host_mob.stat == DEAD || HAS_TRAIT(program.host_mob, TRAIT_FAKEDEATH))
+ return TRUE
+ return FALSE
+
+/datum/nanite_rule/nanites
+ name = "Nanite Volume"
+ desc = "Checks the host's nanite volume."
+
+ var/threshold = 50
+ var/above = TRUE
+
+/datum/nanite_rule/nanites/check_rule()
+ var/nanite_percent = (program.nanites.nanite_volume - program.nanites.safety_threshold)/(program.nanites.max_nanites - program.nanites.safety_threshold)*100
+ if(above)
+ if(nanite_percent >= threshold)
+ return TRUE
+ else
+ if(nanite_percent < threshold)
+ return TRUE
+ return FALSE
+
+/datum/nanite_rule/nanites/copy_to(datum/nanite_program/new_program)
+ var/datum/nanite_rule/nanites/rule = new(new_program)
+ rule.above = above
+ rule.threshold = threshold
+
+/datum/nanite_rule/nanites/display()
+ return "[name] [above ? ">" : "<"] [threshold]%"
+
+/datum/nanite_rule/damage
+ name = "Damage"
+ desc = "Checks the host's damage."
+
+ var/threshold = 50
+ var/above = TRUE
+ var/damage_type = BRUTE
+
+/datum/nanite_rule/damage/check_rule()
+ var/damage_amt = 0
+ switch(damage_type)
+ if(BRUTE)
+ damage_amt = program.host_mob.getBruteLoss()
+ if(BURN)
+ damage_amt = program.host_mob.getFireLoss()
+ if(TOX)
+ damage_amt = program.host_mob.getToxLoss()
+ if(OXY)
+ damage_amt = program.host_mob.getOxyLoss()
+
+ if(above)
+ if(damage_amt >= threshold)
+ return TRUE
+ else
+ if(damage_amt < threshold)
+ return TRUE
+ return FALSE
+
+/datum/nanite_rule/damage/copy_to(datum/nanite_program/new_program)
+ var/datum/nanite_rule/damage/rule = new(new_program)
+ rule.above = above
+ rule.threshold = threshold
+ rule.damage_type = damage_type
+
+/datum/nanite_rule/damage/display()
+ return "[damage_type] [above ? ">" : "<"] [threshold]"
diff --git a/massmeta/features/nanites/code/settings/text.dm b/massmeta/features/nanites/code/settings/text.dm
new file mode 100644
index 0000000000000..f2158f34b9d6c
--- /dev/null
+++ b/massmeta/features/nanites/code/settings/text.dm
@@ -0,0 +1,18 @@
+/datum/nanite_extra_setting/text
+ setting_type = NESTYPE_TEXT
+
+/datum/nanite_extra_setting/text/New(initial)
+ value = initial
+
+/datum/nanite_extra_setting/text/set_value(value)
+ src.value = trim(value)
+
+/datum/nanite_extra_setting/text/get_copy()
+ return new /datum/nanite_extra_setting/text(value)
+
+/datum/nanite_extra_setting/text/get_frontend_list(name)
+ return list(list(
+ "name" = name,
+ "type" = setting_type,
+ "value" = value,
+ ))
diff --git a/massmeta/features/nanites/code/settings/type.dm b/massmeta/features/nanites/code/settings/type.dm
new file mode 100644
index 0000000000000..2085ba510c42a
--- /dev/null
+++ b/massmeta/features/nanites/code/settings/type.dm
@@ -0,0 +1,18 @@
+/datum/nanite_extra_setting/type
+ setting_type = NESTYPE_TYPE
+ var/list/types
+
+/datum/nanite_extra_setting/type/New(initial, types)
+ value = initial
+ src.types = types
+
+/datum/nanite_extra_setting/type/get_copy()
+ return new /datum/nanite_extra_setting/type(value, types)
+
+/datum/nanite_extra_setting/type/get_frontend_list(name)
+ return list(list(
+ "name" = name,
+ "type" = setting_type,
+ "value" = value,
+ "types" = types
+ ))
diff --git a/massmeta/features/nanites/code/spawners.dm b/massmeta/features/nanites/code/spawners.dm
new file mode 100644
index 0000000000000..59b166feaa01c
--- /dev/null
+++ b/massmeta/features/nanites/code/spawners.dm
@@ -0,0 +1,9 @@
+/obj/effect/spawner/random/techstorage/nanite
+ name = "RnD nanite board spawner"
+ loot = list(
+ /obj/item/circuitboard/computer/nanite_chamber_control,
+ /obj/item/circuitboard/computer/nanite_cloud_controller,
+ /obj/item/circuitboard/machine/nanite_chamber,
+ /obj/item/circuitboard/machine/nanite_programmer,
+ /obj/item/circuitboard/machine/nanite_program_hub,
+ )
diff --git a/massmeta/features/nanites/icons/areas.dmi b/massmeta/features/nanites/icons/areas.dmi
new file mode 100644
index 0000000000000..b8f55aa45d606
Binary files /dev/null and b/massmeta/features/nanites/icons/areas.dmi differ
diff --git a/massmeta/features/nanites/icons/computer.dmi b/massmeta/features/nanites/icons/computer.dmi
new file mode 100644
index 0000000000000..ebbf7d90418f2
Binary files /dev/null and b/massmeta/features/nanites/icons/computer.dmi differ
diff --git a/massmeta/features/nanites/icons/decals.dmi b/massmeta/features/nanites/icons/decals.dmi
new file mode 100644
index 0000000000000..a4384c24bd0ec
Binary files /dev/null and b/massmeta/features/nanites/icons/decals.dmi differ
diff --git a/massmeta/features/nanites/icons/disk.dmi b/massmeta/features/nanites/icons/disk.dmi
new file mode 100644
index 0000000000000..4a296f0f5d561
Binary files /dev/null and b/massmeta/features/nanites/icons/disk.dmi differ
diff --git a/massmeta/features/nanites/icons/nanite_actions.dmi b/massmeta/features/nanites/icons/nanite_actions.dmi
new file mode 100644
index 0000000000000..ce2f24a03e2ad
Binary files /dev/null and b/massmeta/features/nanites/icons/nanite_actions.dmi differ
diff --git a/massmeta/features/nanites/icons/nanite_chamber.dmi b/massmeta/features/nanites/icons/nanite_chamber.dmi
new file mode 100644
index 0000000000000..5eec17e72978e
Binary files /dev/null and b/massmeta/features/nanites/icons/nanite_chamber.dmi differ
diff --git a/massmeta/features/nanites/icons/nanite_device.dmi b/massmeta/features/nanites/icons/nanite_device.dmi
new file mode 100644
index 0000000000000..e9cec8222bf4a
Binary files /dev/null and b/massmeta/features/nanites/icons/nanite_device.dmi differ
diff --git a/massmeta/features/nanites/icons/nanite_hud.dmi b/massmeta/features/nanites/icons/nanite_hud.dmi
new file mode 100644
index 0000000000000..84ff0b39d9d06
Binary files /dev/null and b/massmeta/features/nanites/icons/nanite_hud.dmi differ
diff --git a/massmeta/features/nanites/icons/nanite_machines.dmi b/massmeta/features/nanites/icons/nanite_machines.dmi
new file mode 100644
index 0000000000000..8794084841dd9
Binary files /dev/null and b/massmeta/features/nanites/icons/nanite_machines.dmi differ
diff --git a/massmeta/features/nanites/icons/nanites_decal.dmi b/massmeta/features/nanites/icons/nanites_decal.dmi
new file mode 100644
index 0000000000000..a4384c24bd0ec
Binary files /dev/null and b/massmeta/features/nanites/icons/nanites_decal.dmi differ
diff --git a/massmeta/features/nanites/includes.dm b/massmeta/features/nanites/includes.dm
new file mode 100644
index 0000000000000..4b3901d8f575b
--- /dev/null
+++ b/massmeta/features/nanites/includes.dm
@@ -0,0 +1,45 @@
+#include "code\areas.dm"
+#include "code\circuitboards.dm"
+#include "code\items.dm"
+#include "code\machines.dm"
+#include "code\mood_events.dm"
+#include "code\nanite_backup.dm"
+#include "code\nanite_component.dm"
+#include "code\nanite_disease.dm"
+#include "code\nanite_hud.dm"
+#include "code\nanite_remote.dm"
+#include "code\nanite_scanner.dm"
+#include "code\nanite_subsystem.dm"
+#include "code\nanites_decal.dm"
+#include "code\research.dm"
+#include "code\spawners.dm"
+#include "code\designs\_nanite.dm"
+#include "code\designs\_tools.dm"
+#include "code\designs\nanite_augmentation.dm"
+#include "code\designs\nanite_defective.dm"
+#include "code\designs\nanite_medical.dm"
+#include "code\designs\nanite_protocols.dm"
+#include "code\designs\nanite_sensor.dm"
+#include "code\designs\nanite_suppression.dm"
+#include "code\designs\nanite_utilities.dm"
+#include "code\designs\nanite_weaponized.dm"
+#include "code\machines\nanite_chamber.dm"
+#include "code\machines\nanite_chamber_control.dm"
+#include "code\machines\nanite_cloud_control.dm"
+#include "code\machines\nanite_program_hub.dm"
+#include "code\machines\nanite_programmer.dm"
+#include "code\programs\_programs.dm"
+#include "code\programs\buffing.dm"
+#include "code\programs\healing.dm"
+#include "code\programs\protocols.dm"
+#include "code\programs\rogue.dm"
+#include "code\programs\sensor.dm"
+#include "code\programs\suppression.dm"
+#include "code\programs\utility.dm"
+#include "code\programs\weapon.dm"
+#include "code\settings\extra_settings.dm"
+#include "code\settings\rules.dm"
+#include "code\settings\text.dm"
+#include "code\settings\type.dm"
+#include "code\Z_edits\hud_edits.dm"
+#include "code\Z_edits\research_edits.dm"
diff --git a/massmeta/features/nanites/readme.md b/massmeta/features/nanites/readme.md
new file mode 100644
index 0000000000000..583a3abc7e676
--- /dev/null
+++ b/massmeta/features/nanites/readme.md
@@ -0,0 +1,35 @@
+## Module ID: NANITES
+
+### Description:
+
+Добавляет ниниты.
+
+
+### TG Proc/File Changes:
+
+- N/A
+
+
+### Modular Overrides:
+
+- N/A
+
+
+### Defines:
+
+~meta_defines/nanites.dm
+~meta_defines/_signals/nanites.dm
+
+
+### TGUI Files:
+
+- tgui\packages\tgui\interfaces\NaniteChamberControl.tsx
+- tgui\packages\tgui\interfaces\NaniteCloudControl.tsx
+- tgui\packages\tgui\interfaces\NaniteProgramHub.tsx
+- tgui\packages\tgui\interfaces\NaniteProgrammer.tsx
+- tgui\packages\tgui\interfaces\NaniteRemote.tsx
+
+
+### Credits:
+
+- https://github.com/fulpstation/fulpstation/pull/1100 - original PR
diff --git a/massmeta/features/nanites/sound/nanite_chamber.wav b/massmeta/features/nanites/sound/nanite_chamber.wav
new file mode 100644
index 0000000000000..c75e6fbb77adb
Binary files /dev/null and b/massmeta/features/nanites/sound/nanite_chamber.wav differ
diff --git a/massmeta/features/nanites/sound/nanite_install.wav b/massmeta/features/nanites/sound/nanite_install.wav
new file mode 100644
index 0000000000000..c6e8ed77888a9
Binary files /dev/null and b/massmeta/features/nanites/sound/nanite_install.wav differ
diff --git a/massmeta/features/nanites/sound/nanite_install_short.mp3 b/massmeta/features/nanites/sound/nanite_install_short.mp3
new file mode 100644
index 0000000000000..c653a03398213
Binary files /dev/null and b/massmeta/features/nanites/sound/nanite_install_short.mp3 differ
diff --git a/massmeta/features/nanites/sound/nanite_scan.mp3 b/massmeta/features/nanites/sound/nanite_scan.mp3
new file mode 100644
index 0000000000000..61efa46dc6ac6
Binary files /dev/null and b/massmeta/features/nanites/sound/nanite_scan.mp3 differ
diff --git a/massmeta/modular_meta.dm b/massmeta/modular_meta.dm
index 443a6a9aa55cf..9bc19d6a34897 100644
--- a/massmeta/modular_meta.dm
+++ b/massmeta/modular_meta.dm
@@ -15,6 +15,7 @@
#include "features\additional_circuit\includes.dm"
#include "features\hardsuits\includes.dm"
#include "features\kvass\includes.dm"
+#include "features\nanites\includes.dm"
#include "features\smites\includes.dm"
#include "features\soviet_crate\includes.dm"
// END_INCLUDE
diff --git a/massmeta/modular_meta_defines.dm b/massmeta/modular_meta_defines.dm
index 899edc4d54590..af0c13508441c 100644
--- a/massmeta/modular_meta_defines.dm
+++ b/massmeta/modular_meta_defines.dm
@@ -7,4 +7,6 @@
// BEGIN_INCLUDE
#include "~meta_defines\butt_furrt.dm"
#include "~meta_defines\inventory.dm"
+#include "~meta_defines\nanites.dm"
+#include "~meta_defines\_signals\nanites.dm"
// END_INCLUDE
diff --git a/massmeta/~meta_defines/_signals/nanites.dm b/massmeta/~meta_defines/_signals/nanites.dm
new file mode 100644
index 0000000000000..4949766ab4f80
--- /dev/null
+++ b/massmeta/~meta_defines/_signals/nanites.dm
@@ -0,0 +1,36 @@
+///() returns TRUE if nanites have stealth
+#define COMSIG_NANITE_IS_STEALTHY "nanite_is_stealthy"
+///() deletes the nanite component
+#define COMSIG_NANITE_DELETE "nanite_delete"
+///() makes the input list a copy the nanites' program list
+#define COMSIG_NANITE_GET_PROGRAMS "nanite_get_programs"
+///(amount) Returns nanite amount
+#define COMSIG_NANITE_GET_VOLUME "nanite_get_volume"
+///(amount) Sets current nanite volume to the given amount
+#define COMSIG_NANITE_SET_VOLUME "nanite_set_volume"
+///(amount) Adjusts nanite volume by the given amount
+#define COMSIG_NANITE_ADJUST_VOLUME "nanite_adjust"
+///(amount) Sets maximum nanite volume to the given amount
+#define COMSIG_NANITE_SET_MAX_VOLUME "nanite_set_max_volume"
+///(amount(0-100)) Sets cloud ID to the given amount
+#define COMSIG_NANITE_SET_CLOUD "nanite_set_cloud"
+///(amount) Sets safety threshold to the given amount
+#define COMSIG_NANITE_SET_SAFETY "nanite_set_safety"
+///(amount) Sets regeneration rate to the given amount
+#define COMSIG_NANITE_SET_REGEN "nanite_set_regen"
+///(code(1-9999)) Called when sending a nanite signal to a mob.
+#define COMSIG_NANITE_SIGNAL "nanite_signal"
+///(comm_code(1-9999), comm_message) Called when sending a nanite comm signal to a mob.
+#define COMSIG_NANITE_COMM_SIGNAL "nanite_comm_signal"
+///(mob/user, full_scan) - sends to chat a scan of the nanites to the user, returns TRUE if nanites are detected
+#define COMSIG_NANITE_SCAN "nanite_scan"
+///(list/data, scan_level) - adds nanite data to the given data list - made for ui_data procs
+#define COMSIG_NANITE_UI_DATA "nanite_ui_data"
+///(datum/nanite_program/new_program, datum/nanite_program/source_program) Called when adding a program to a nanite component
+#define COMSIG_NANITE_ADD_PROGRAM "nanite_add_program"
+ ///Installation successful
+ #define COMPONENT_PROGRAM_INSTALLED 1
+ ///Installation failed, but there are still nanites
+ #define COMPONENT_PROGRAM_NOT_INSTALLED 2
+///(datum/component/nanites, full_overwrite, copy_activation) Called to sync the target's nanites to a given nanite component
+#define COMSIG_NANITE_SYNC "nanite_sync"
diff --git a/massmeta/~meta_defines/nanites.dm b/massmeta/~meta_defines/nanites.dm
new file mode 100644
index 0000000000000..fe748750c5694
--- /dev/null
+++ b/massmeta/~meta_defines/nanites.dm
@@ -0,0 +1,50 @@
+///The research point type that Nanites generates.
+#define TECHWEB_POINT_TYPE_NANITES "Nanite Research"
+
+///Trait given by Nanites
+#define TRAIT_NANITES "Nanites"
+
+#define NANITE_SHOCK_IMMUNE (1<<0)
+#define NANITE_EMP_IMMUNE (1<<1)
+
+///Nanite Protocol types
+#define NANITE_PROTOCOL_REPLICATION "nanite_replication"
+#define NANITE_PROTOCOL_STORAGE "nanite_storage"
+
+///Nanite extra settings types: used to help uis know what type an extra setting is
+#define NESTYPE_TEXT "text"
+#define NESTYPE_NUMBER "number"
+#define NESTYPE_TYPE "type"
+#define NESTYPE_BOOLEAN "boolean"
+
+///Nanite Extra Settings - Note that these will also be the names displayed in the UI
+#define NES_SENT_CODE "Sent Code"
+#define NES_DELAY "Delay"
+#define NES_COMM_CODE "Comm Code"
+#define NES_RELAY_CHANNEL "Relay Channel"
+#define NES_HEALTH_PERCENT "Health Percent"
+#define NES_NANITE_PERCENT "Nanite Percent"
+#define NES_DIRECTION "Direction"
+#define NES_DAMAGE_TYPE "Damage Type"
+#define NES_DAMAGE "Damage"
+#define NES_MESSAGE "Message"
+#define NES_DIRECTIVE "Directive"
+#define NES_SENTENCE "Sentence"
+#define NES_INCLUSIVE_MODE "Inclusive Mode"
+#define NES_RACE "Race"
+#define NES_MODE "Mode"
+#define NES_MOOD_MESSAGE "Mood Message"
+#define NES_PROGRAM_OVERWRITE "Program Overwrite"
+#define NES_CLOUD_OVERWRITE "Cloud Overwrite"
+#define NES_SCAN_TYPE "Scan Type"
+#define NES_BUTTON_NAME "Button Name"
+#define NES_ICON "Icon"
+
+#define NANITE_CATEGORY_UTILITIES "Utility Nanites"
+#define NANITE_CATEGORY_MEDICAL "Medical Nanites"
+#define NANITES_CATEGORY_SENSOR "Sensor Nanites"
+#define NANITES_CATEGORY_AUGMENTATION "Augmentation Nanites"
+#define NANITES_CATEGORY_SUPPRESSION "Suppression Nanites"
+#define NANITES_CATEGORY_WEAPONIZED "Weaponized Nanites"
+#define NANITES_CATEGORY_PROTOCOLS "Protocols Nanites"
+#define NANITES_CATEGORY_DEFECTIVE "Defective Nanites"
diff --git a/tgui/packages/tgui/interfaces/NaniteChamberControl.tsx b/tgui/packages/tgui/interfaces/NaniteChamberControl.tsx
new file mode 100644
index 0000000000000..7ccb45d557f65
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/NaniteChamberControl.tsx
@@ -0,0 +1,314 @@
+import { BooleanLike } from 'common/react';
+
+import { useBackend } from '../backend';
+import {
+ Box,
+ Button,
+ Collapsible,
+ LabeledList,
+ NoticeBox,
+ NumberInput,
+ Section,
+ Table,
+} from '../components';
+import { Window } from '../layouts';
+
+type Data = {
+ status_msg: string;
+ locked: BooleanLike;
+ occupant_name: string;
+ has_nanites: BooleanLike;
+ nanite_volume: number;
+ regen_rate: number;
+ safety_threshold: number;
+ cloud_id: number;
+ scan_level: number;
+ mob_programs: MobData[];
+};
+
+type MobData = {
+ extra_settings: ExtraSettingsData[];
+ rules: RulesData[];
+ name: string;
+ desc: string;
+ activated: BooleanLike;
+ use_rate: number;
+ can_trigger: BooleanLike;
+ trigger_cost: number;
+ trigger_cooldown: number;
+ timer_trigger_delay: number;
+ timer_trigger: number;
+ timer_restart: number;
+ timer_shutdown: number;
+ has_extra_settings: BooleanLike;
+ activation_code: number;
+ deactivation_code: number;
+ kill_code: number;
+ trigger_code: number;
+ has_rules: BooleanLike;
+};
+
+type ExtraSettingsData = {
+ name: string;
+ value: string;
+};
+
+type RulesData = {
+ display: string;
+};
+
+export const NaniteChamberControl = (props) => {
+ return (
+
+
+
+
+
+ );
+};
+
+const NaniteChamberControlContent = (props) => {
+ const { act, data } = useBackend();
+ const {
+ status_msg,
+ locked,
+ occupant_name,
+ has_nanites,
+ nanite_volume,
+ regen_rate,
+ safety_threshold,
+ cloud_id,
+ scan_level,
+ mob_programs = [],
+ } = data;
+
+ if (status_msg) {
+ return {status_msg};
+ }
+
+ return (
+ act('toggle_lock')}
+ />
+ }
+ >
+ {!has_nanites ? (
+ <>
+
+ No Nanites Detected
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/NaniteCloudControl.tsx b/tgui/packages/tgui/interfaces/NaniteCloudControl.tsx
new file mode 100644
index 0000000000000..c0392c6343a18
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/NaniteCloudControl.tsx
@@ -0,0 +1,375 @@
+import { BooleanLike } from 'common/react';
+
+import { useBackend } from '../backend';
+import {
+ Box,
+ Button,
+ Collapsible,
+ LabeledList,
+ NoticeBox,
+ NumberInput,
+ Section,
+ Table,
+} from '../components';
+import { Window } from '../layouts';
+
+type Data = {
+ has_disk: BooleanLike;
+ has_program: BooleanLike;
+ disk_data: ProgramData[];
+ new_backup_id: number;
+ current_view: number;
+ cloud_backup: BooleanLike;
+ can_rule: BooleanLike;
+ cloud_programs: ProgramData[];
+ cloud_backups: CloudBackupData[];
+};
+
+type ProgramData = {
+ name: string;
+ desc: string;
+ id: number;
+ use_rate: number;
+ can_trigger: BooleanLike;
+ trigger_cost: number;
+ trigger_cooldown: number;
+ activated: BooleanLike;
+ activation_code: number;
+ deactivation_code: number;
+ kill_code: number;
+ trigger_code: number;
+ timer_restart: number;
+ timer_shutdown: number;
+ timer_trigger: number;
+ timer_trigger_delay: number;
+ has_rules: BooleanLike;
+ all_rules_required: BooleanLike;
+ rules: RuleData[];
+ extra_settings: ExtraSettingsData[];
+ has_extra_settings: BooleanLike;
+};
+
+type ExtraSettingsData = {
+ name: string;
+ type: string;
+ value: string;
+ unit: string;
+ true_text: string;
+ false_text: string;
+};
+
+type RuleData = {
+ display: string;
+ program_id: number;
+ id: number;
+};
+
+type CloudBackupData = {
+ cloud_id: number;
+};
+
+const NaniteDiskBox = (props) => {
+ const { data } = useBackend();
+ const { has_disk, has_program, disk_data } = data;
+ if (!has_disk) {
+ return No disk inserted;
+ }
+ if (!has_program) {
+ return Inserted disk has no program;
+ }
+ return ;
+};
+
+const NaniteInfoBox = (props) => {
+ const { act } = useBackend();
+ const { program } = props;
+ const {
+ name,
+ desc,
+ activated,
+ use_rate,
+ can_trigger,
+ trigger_cost,
+ trigger_cooldown,
+ activation_code,
+ deactivation_code,
+ kill_code,
+ trigger_code,
+ timer_restart,
+ timer_shutdown,
+ timer_trigger,
+ timer_trigger_delay,
+ extra_settings = [],
+ rules = [],
+ } = program;
+ return (
+
+ {activated ? 'Activated' : 'Deactivated'}
+
+ }
+ >
+
+ {desc}
+
+
+ {use_rate}
+ {!!can_trigger && (
+ <>
+
+ {trigger_cost}
+
+
+ {trigger_cooldown}
+
+ >
+ )}
+
+
+
+
+
+
+
+
+ {activation_code}
+
+
+ {deactivation_code}
+
+ {kill_code}
+ {!!can_trigger && (
+
+ {trigger_code}
+
+ )}
+
+
+
+
+
+
+
+ {timer_restart} s
+
+
+ {timer_shutdown} s
+
+ {!!can_trigger && (
+ <>
+
+ {timer_trigger} s
+
+
+ {timer_trigger_delay} s
+
+ >
+ )}
+
+
+
+
+
+
+ {extra_settings.map((setting) => {
+ const naniteTypesDisplayMap = {
+ number: (
+ <>
+ {setting.value}
+ {setting.unit}
+ >
+ ),
+ text: setting.value,
+ type: setting.value,
+ boolean: setting.value ? setting.true_text : setting.false_text,
+ };
+ return (
+
+ {naniteTypesDisplayMap[setting.type]}
+
+ );
+ })}
+
+
+
+ );
+};
+
+const NaniteCloudBackupDetails = (props) => {
+ const { act, data } = useBackend();
+ const { current_view, has_program, can_rule, cloud_backup, cloud_programs } =
+ data;
+ if (!cloud_backup) {
+ return ERROR: Backup not found;
+ }
+ return (
+ act('upload_program')}
+ />
+ )
+ }
+ >
+ {cloud_programs.map((program) => {
+ return (
+
+ act('remove_program', {
+ program_id: program.id,
+ })
+ }
+ />
+ }
+ >
+
+
+
+ {!!can_rule && (
+
+
+
+ );
+ })}
+
+ );
+};
+
+export const NaniteCloudControl = (props) => {
+ const { act, data } = useBackend();
+ const { has_disk, current_view, new_backup_id, cloud_backups } = data;
+ return (
+
+
+ act('eject')}
+ />
+ }
+ >
+
+
+
+ act('set_view', {
+ view: 0,
+ })
+ }
+ />
+ ) : (
+ <>
+ {'New Backup: '}
+
+ act('update_new_backup_value', {
+ value: value,
+ })
+ }
+ />
+ act('create_backup')} />
+ >
+ )
+ }
+ >
+ {!current_view ? (
+ cloud_backups.map((backup) => (
+
+ act('set_view', {
+ view: backup.cloud_id,
+ })
+ }
+ />
+ ))
+ ) : (
+
+ )}
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/NaniteProgramHub.tsx b/tgui/packages/tgui/interfaces/NaniteProgramHub.tsx
new file mode 100644
index 0000000000000..fe0cb7a4fb084
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/NaniteProgramHub.tsx
@@ -0,0 +1,174 @@
+import { BooleanLike } from 'common/react';
+
+import { useBackend, useSharedState } from '../backend';
+import {
+ Button,
+ Flex,
+ LabeledList,
+ NoticeBox,
+ Section,
+ Tabs,
+} from '../components';
+import { Window } from '../layouts';
+
+type Data = {
+ detail_view: string;
+ disk: DiskData;
+ has_disk: BooleanLike;
+ has_program: BooleanLike;
+ programs: ProgramData[];
+ categories: string[];
+};
+
+type DiskData = {
+ name: string;
+ desc: string;
+};
+
+type ProgramData = {
+ name: string;
+ desc: string;
+ id: string;
+};
+
+export const NaniteProgramHub = (props, context) => {
+ const { act, data } = useBackend();
+ const {
+ detail_view,
+ disk,
+ has_disk,
+ has_program,
+ programs = [],
+ categories,
+ } = data;
+ const [selectedCategory, setSelectedCategory] = useSharedState(
+ context,
+ 'category',
+ );
+ const programsInCategory = (programs && programs[selectedCategory]) || [];
+
+ return (
+
+
+
+ act('eject')}
+ />
+ act('clear')}
+ />
+ >
+ }
+ >
+ {has_disk ? (
+ has_program ? (
+
+
+ {disk.name}
+
+
+ {disk.desc}
+
+
+ ) : (
+ No Program Installed
+ )
+ ) : (
+ Insert Disk
+ )}
+
+
+ act('toggle_details')}
+ />
+ act('refresh')}
+ />
+ >
+ }
+ >
+ {programs === null ? (
+ No nanite programs are currently researched.
+ ) : (
+
+
+
+ {categories.map((category) => (
+ {
+ setSelectedCategory(category);
+ }}
+ >
+ {category.substring(0, category.length - 8)}
+
+ ))}
+
+
+
+ {detail_view ? (
+ programsInCategory.map((program) => (
+
+ act('download', {
+ program_id: program.id,
+ })
+ }
+ />
+ }
+ >
+ {program.desc}
+
+ ))
+ ) : (
+
+ {programsInCategory.map((program) => (
+
+ act('download', {
+ program_id: program.id,
+ })
+ }
+ />
+ }
+ />
+ ))}
+
+ )}
+
+
+ )}
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/NaniteProgrammer.tsx b/tgui/packages/tgui/interfaces/NaniteProgrammer.tsx
new file mode 100644
index 0000000000000..cbd69a9666b9d
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/NaniteProgrammer.tsx
@@ -0,0 +1,394 @@
+import { BooleanLike } from 'common/react';
+
+import { useBackend } from '../backend';
+import {
+ Button,
+ Dropdown,
+ Input,
+ LabeledList,
+ NoticeBox,
+ NumberInput,
+ Section,
+ Table,
+} from '../components';
+import { Window } from '../layouts';
+
+type Data = {
+ has_disk: BooleanLike;
+ has_program: BooleanLike;
+ name: string;
+ desc: string;
+ use_rate: string;
+ can_trigger: BooleanLike;
+ trigger_cost: number;
+ trigger_cooldown: number;
+ activated: BooleanLike;
+ activation_code: number;
+ deactivation_code: number;
+ kill_code: number;
+ trigger_code: number;
+ timer_restart: number;
+ timer_shutdown: number;
+ timer_trigger: number;
+ timer_trigger_delay: number;
+ has_extra_settings: BooleanLike;
+ extra_settings: ExtraSettingsData[];
+};
+
+type ExtraSettingsData = {
+ name: string;
+ setting: string;
+};
+
+const NaniteCodes = (props) => {
+ const { act, data } = useBackend();
+ const {
+ activation_code,
+ deactivation_code,
+ kill_code,
+ can_trigger,
+ trigger_code,
+ } = data;
+ return (
+
+
+
+
+ act('set_code', {
+ target_code: 'activation',
+ code: value,
+ })
+ }
+ />
+
+
+
+ act('set_code', {
+ target_code: 'deactivation',
+ code: value,
+ })
+ }
+ />
+
+
+
+ act('set_code', {
+ target_code: 'kill',
+ code: value,
+ })
+ }
+ />
+
+ {!!can_trigger && (
+
+
+ act('set_code', {
+ target_code: 'trigger',
+ code: value,
+ })
+ }
+ />
+
+ )}
+
+
+ );
+};
+
+const NaniteDelays = (props) => {
+ const { act, data } = useBackend();
+ const {
+ timer_restart,
+ timer_shutdown,
+ can_trigger,
+ timer_trigger,
+ timer_trigger_delay,
+ } = data;
+ return (
+
+
+
+
+ act('set_restart_timer', {
+ delay: value,
+ })
+ }
+ />
+
+
+
+ act('set_shutdown_timer', {
+ delay: value,
+ })
+ }
+ />
+
+ {!!can_trigger && (
+ <>
+
+
+ act('set_trigger_timer', {
+ delay: value,
+ })
+ }
+ />
+
+
+
+ act('set_timer_trigger_delay', {
+ delay: value,
+ })
+ }
+ />
+
+ >
+ )}
+
+
+ );
+};
+
+const NaniteExtraEntry = (props) => {
+ const { extra_setting } = props;
+ const { name, type } = extra_setting;
+ const typeComponentMap = {
+ number: ,
+ text: ,
+ type: ,
+ boolean: ,
+ };
+ return (
+ {typeComponentMap[type]}
+ );
+};
+
+const NaniteExtraNumber = (props) => {
+ const { extra_setting } = props;
+ const { act } = useBackend();
+ const { name, value, min, max, unit } = extra_setting;
+ return (
+
+ act('set_extra_setting', {
+ target_setting: name,
+ value: val,
+ })
+ }
+ />
+ );
+};
+
+const NaniteExtraText = (props) => {
+ const { extra_setting } = props;
+ const { act } = useBackend();
+ const { name, value } = extra_setting;
+ return (
+
+ act('set_extra_setting', {
+ target_setting: name,
+ value: val,
+ })
+ }
+ />
+ );
+};
+
+const NaniteExtraType = (props) => {
+ const { extra_setting } = props;
+ const { act } = useBackend();
+ const { name, value, types } = extra_setting;
+ return (
+
+ act('set_extra_setting', {
+ target_setting: name,
+ value: val,
+ })
+ }
+ />
+ );
+};
+
+const NaniteExtraBoolean = (props) => {
+ const { extra_setting } = props;
+ const { act } = useBackend();
+ const { name, value, true_text, false_text } = extra_setting;
+ return (
+
+ act('set_extra_setting', {
+ target_setting: name,
+ })
+ }
+ />
+ );
+};
+
+export const NaniteProgrammer = (props) => {
+ return (
+
+
+
+
+
+ );
+};
+
+const NaniteProgrammerContent = (props) => {
+ const { act, data } = useBackend();
+ const {
+ has_disk,
+ has_program,
+ name,
+ desc,
+ use_rate,
+ can_trigger,
+ trigger_cost,
+ trigger_cooldown,
+ activated,
+ has_extra_settings,
+ extra_settings = [],
+ } = data;
+ if (!has_disk) {
+ return (
+ Insert a nanite program disk
+ );
+ }
+ if (!has_program) {
+ return (
+ act('eject')} />
+ }
+ />
+ );
+ }
+ return (
+ act('eject')} />
+ }
+ >
+
+
+ {desc}
+
+
+ {use_rate}
+ {!!can_trigger && (
+ <>
+
+ {trigger_cost}
+
+
+ {trigger_cooldown}
+
+ >
+ )}
+
+
+
+
+ act('toggle_active')}
+ />
+ }
+ >
+
+ {!!has_extra_settings && (
+
+
+ {extra_settings.map((setting) => (
+
+ ))}
+
+
+ )}
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/NaniteRemote.tsx b/tgui/packages/tgui/interfaces/NaniteRemote.tsx
new file mode 100644
index 0000000000000..03b9f44eb8d83
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/NaniteRemote.tsx
@@ -0,0 +1,198 @@
+import { BooleanLike } from 'common/react';
+
+import { useBackend } from '../backend';
+import {
+ Button,
+ Input,
+ LabeledList,
+ NoticeBox,
+ NumberInput,
+ Section,
+ Table,
+} from '../components';
+import { Window } from '../layouts';
+
+export const NaniteRemote = (props) => {
+ return (
+
+
+
+
+
+ );
+};
+
+type Data = {
+ code: number;
+ locked: BooleanLike;
+ mode: string;
+ program_name: string;
+ relay_code: number;
+ comms: string;
+ message: string;
+ saved_settings: SettingsData[];
+};
+
+type SettingsData = {
+ id: number;
+ name: string;
+ mode: string;
+ relay_code: number;
+ code: number;
+};
+
+export const NaniteRemoteContent = (props) => {
+ const { act, data } = useBackend();
+ const {
+ code,
+ locked,
+ mode,
+ program_name,
+ relay_code,
+ comms,
+ message,
+ saved_settings = [],
+ } = data;
+
+ const modes = ['Off', 'Local', 'Targeted', 'Area', 'Relay'];
+
+ if (locked) {
+ return This interface is locked.;
+ }
+
+ return (
+ <>
+ act('lock')}
+ />
+ }
+ >
+
+
+
+ act('update_name', {
+ name: value,
+ })
+ }
+ />
+ act('save')} />
+
+
+
+ act('set_code', {
+ code: value,
+ })
+ }
+ />
+
+ {!!comms && (
+
+
+ act('set_message', {
+ value: value,
+ })
+ }
+ />
+
+ )}
+ {mode === 'Relay' && (
+
+
+ act('set_relay_code', {
+ code: value,
+ })
+ }
+ />
+
+ )}
+
+ {modes.map((key) => (
+
+ act('select_mode', {
+ mode: key,
+ })
+ }
+ />
+ ))}
+
+
+
+
+ {saved_settings.length > 0 ? (
+
+
+ Name
+ Mode
+ Code
+ Relay
+
+ {saved_settings.map((setting) => (
+
+
+ {setting.name}:
+
+ {setting.mode}
+ {setting.code}
+
+ {setting.mode === 'Relay' && setting.relay_code}
+
+
+
+ act('load', {
+ save_id: setting.id,
+ })
+ }
+ />
+
+ act('remove_save', {
+ save_id: setting.id,
+ })
+ }
+ />
+
+
+ ))}
+
+ ) : (
+ No settings currently saved
+ )}
+
+ >
+ );
+};