diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index 27dea6817b4..6acfad93ef7 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -474,6 +474,7 @@ GLOBAL_LIST_INIT(pda_styles, sortList(list(MONO, VT, ORBITRON, SHARE)))
#define VOMIT_TOXIC 1
#define VOMIT_PURPLE 2
+#define VOMIT_NANITE 3
//chem grenades defines
#define GRENADE_EMPTY 1
diff --git a/code/__DEFINES/nanites.dm b/code/__DEFINES/nanites.dm
index 40611465f44..63301898796 100644
--- a/code/__DEFINES/nanites.dm
+++ b/code/__DEFINES/nanites.dm
@@ -12,6 +12,11 @@
#define NANITE_CLOUD_DISABLE 2
#define NANITE_CLOUD_ENABLE 3
+//Nanite excess thresholds
+#define NANITE_EXCESS_MINOR 25
+#define NANITE_EXCESS_VOMIT 100
+#define NANITE_EXCESS_BURST 350
+
///Nanite Protocol types
#define NANITE_PROTOCOL_REPLICATION "nanite_replication"
#define NANITE_PROTOCOL_STORAGE "nanite_storage"
diff --git a/code/datums/components/nanites.dm b/code/datums/components/nanites.dm
index 7c4b369dd11..426ca8c878d 100644
--- a/code/datums/components/nanites.dm
+++ b/code/datums/components/nanites.dm
@@ -121,13 +121,13 @@
next_sync = world.time + NANITE_SYNC_DELAY
set_nanite_bar()
-
+///Deletes nanites!
/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)
+///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
@@ -149,6 +149,7 @@
var/datum/nanite_program/SNP = X
add_program(null, SNP.copy())
+///Syncs the nanites to their assigned cloud copy, if it is available. If it is not, there is a small chance of a software error instead.
/datum/component/nanites/proc/cloud_sync()
if(cloud_id)
var/datum/nanite_cloud_backup/backup = SSnanites.get_cloud_backup(cloud_id)
@@ -162,6 +163,7 @@
var/datum/nanite_program/NP = pick(programs)
NP.software_error()
+///Adds a nanite program, replacing existing unique programs of the same type. A source program can be specified to copy its programming onto the new one.
/datum/component/nanites/proc/add_program(datum/source, datum/nanite_program/new_program, datum/nanite_program/source_program)
SIGNAL_HANDLER
@@ -183,13 +185,67 @@
adjust_nanites(null, -amount)
return (nanite_volume > 0)
+///Modifies the current nanite volume, then checks if the nanites are depleted or exceeding the maximum amount
/datum/component/nanites/proc/adjust_nanites(datum/source, amount)
SIGNAL_HANDLER
- nanite_volume = clamp(nanite_volume + amount, 0, max_nanites)
+ nanite_volume += amount
+ if(nanite_volume > max_nanites)
+ reject_excess_nanites()
if(nanite_volume <= 0) //oops we ran out
qdel(src)
+/**
+ * Handles how nanites leave the host's body if they find out that they're currently exceeding the maximum supported amount
+ *
+ * IC explanation:
+ * Normally nanites simply discard excess volume by slowing replication or 'sweating' it out in imperceptible amounts,
+ * but if there is a large excess volume, likely due to a programming change that leaves them unable to support their current volume,
+ * the nanites attempt to leave the host as fast as necessary to prevent nanite poisoning. This can range from minor oozing to nanites
+ * rapidly bursting out from every possible pathway, causing temporary inconvenience to the host.
+ */
+/datum/component/nanites/proc/reject_excess_nanites()
+ var/excess = nanite_volume - max_nanites
+ nanite_volume = max_nanites
+
+ switch(excess)
+ if(0 to NANITE_EXCESS_MINOR) //Minor excess amount, the extra nanites are quietly expelled without visible effects
+ return
+ if((NANITE_EXCESS_MINOR + 0.1) to NANITE_EXCESS_VOMIT) //Enough nanites getting rejected at once to be visible to the naked eye
+ host_mob.visible_message("A grainy grey slurry starts oozing out of [host_mob].", "A grainy grey slurry starts oozing out of your skin.", null, 4);
+ if((NANITE_EXCESS_VOMIT + 0.1) to NANITE_EXCESS_BURST) //Nanites getting rejected in massive amounts, but still enough to make a semi-orderly exit through vomit
+ if(iscarbon(host_mob))
+ var/mob/living/carbon/C = host_mob
+ host_mob.visible_message("[host_mob] vomits a grainy grey slurry!", "You suddenly vomit a metallic-tasting grainy grey slurry!", null);
+ C.vomit(0, FALSE, TRUE, FLOOR(excess / 100, 1), FALSE, VOMIT_NANITE, FALSE, TRUE, 0)
+ else
+ host_mob.visible_message("A metallic grey slurry bursts out of [host_mob]'s skin!", "A metallic grey slurry violently bursts out of your skin!", null);
+ if(isturf(host_mob.drop_location()))
+ var/turf/T = host_mob.drop_location()
+ T.add_vomit_floor(host_mob, VOMIT_NANITE, 0)
+ if((NANITE_EXCESS_BURST + 0.1) to INFINITY) //Way too many nanites, they just leave through the closest exit before they harm/poison the host
+ host_mob.visible_message("A torrent of metallic grey slurry violently bursts out of [host_mob]'s face and floods out of [host_mob.p_their()] skin!",
+ "A torrent of metallic grey slurry violently bursts out of your eyes, ears, and mouth, and floods out of your skin!");
+
+ host_mob.blind_eyes(15) //nanites coming out of your eyes
+ host_mob.Paralyze(120)
+ if(iscarbon(host_mob))
+ var/mob/living/carbon/C = host_mob
+ var/obj/item/organ/ears/ears = C.getorganslot(ORGAN_SLOT_EARS)
+ if(ears)
+ ears.adjustEarDamage(0, 30) //nanites coming out of your ears
+ C.vomit(0, FALSE, TRUE, 2, FALSE, VOMIT_NANITE, FALSE, TRUE, 0) //nanites coming out of your mouth
+
+ //nanites everywhere
+ if(isturf(host_mob.drop_location()))
+ var/turf/T = host_mob.drop_location()
+ T.add_vomit_floor(host_mob, VOMIT_NANITE, 0)
+ for(var/turf/adjacent_turf in oview(host_mob, 1))
+ if(adjacent_turf.density || !adjacent_turf.Adjacent(T))
+ continue
+ adjacent_turf.add_vomit_floor(host_mob, VOMIT_NANITE, 0)
+
+///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)
@@ -285,7 +341,7 @@
/datum/component/nanites/proc/set_max_volume(datum/source, amount)
SIGNAL_HANDLER
- max_nanites = max(1, max_nanites)
+ max_nanites = max(1, amount)
/datum/component/nanites/proc/set_cloud(datum/source, amount)
SIGNAL_HANDLER
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 2c414c110d9..9a99a2c4b87 100755
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -540,7 +540,7 @@ GLOBAL_LIST_EMPTY(station_turfs)
/turf/AllowDrop()
return TRUE
-/turf/proc/add_vomit_floor(mob/living/M, toxvomit = NONE, purge = FALSE)
+/turf/proc/add_vomit_floor(mob/living/M, toxvomit = NONE, purge_ratio = 0.1)
var/obj/effect/decal/cleanable/vomit/V = new /obj/effect/decal/cleanable/vomit(src, M.get_static_viruses())
@@ -549,21 +549,23 @@ GLOBAL_LIST_EMPTY(station_turfs)
V = locate() in src
if(!V)
return
- // Make toxins and blazaam vomit look different
+ // Apply the proper icon set based on vomit type
if(toxvomit == VOMIT_PURPLE)
V.icon_state = "vomitpurp_[pick(1,4)]"
else if (toxvomit == VOMIT_TOXIC)
V.icon_state = "vomittox_[pick(1,4)]"
- if (iscarbon(M))
- clear_reagents_to_vomit_pool(M, V, purge)
+ else if (toxvomit == VOMIT_NANITE)
+ V.name = "metallic slurry"
+ V.desc = "A puddle of metallic slurry that looks vaguely like very fine sand. It almost seems like it's moving..."
+ V.icon_state = "vomitnanite_[pick(1,4)]"
+ if (purge_ratio && iscarbon(M))
+ clear_reagents_to_vomit_pool(M, V, purge_ratio)
-/proc/clear_reagents_to_vomit_pool(mob/living/carbon/M, obj/effect/decal/cleanable/vomit/V, purge = FALSE)
+/proc/clear_reagents_to_vomit_pool(mob/living/carbon/M, obj/effect/decal/cleanable/vomit/V, purge_ratio = 0.1)
var/obj/item/organ/stomach/belly = M.getorganslot(ORGAN_SLOT_STOMACH)
if(!belly?.reagents.total_volume)
return
- var/chemicals_lost = belly.reagents.total_volume * 0.1
- if(purge)
- chemicals_lost = belly.reagents.total_volume * 0.67 //For detoxification surgery, we're manually pumping the stomach out of chemcials, so it's far more efficient.
+ var/chemicals_lost = belly.reagents.total_volume * purge_ratio
belly.reagents.trans_to(V, chemicals_lost, transfered_by = M)
//clear the stomach of anything even not food
for(var/bile in belly.reagents.reagent_list)
diff --git a/code/modules/antagonists/changeling/powers/panacea.dm b/code/modules/antagonists/changeling/powers/panacea.dm
index 5ba9410eaa5..6a057d74176 100644
--- a/code/modules/antagonists/changeling/powers/panacea.dm
+++ b/code/modules/antagonists/changeling/powers/panacea.dm
@@ -23,7 +23,7 @@
O.Remove(user)
if(iscarbon(user))
var/mob/living/carbon/C = user
- C.vomit(0, toxic = TRUE)
+ C.vomit(0)
O.forceMove(get_turf(user))
user.reagents.add_reagent(/datum/reagent/medicine/mutadone, 10)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 7a7e9c00290..c66cba96e5e 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -449,7 +449,7 @@
return 0
return ..()
-/mob/living/carbon/proc/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, toxic = FALSE, harm = TRUE, force = FALSE, purge = FALSE)
+/mob/living/carbon/proc/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, vomit_type = VOMIT_TOXIC, harm = TRUE, force = FALSE, purge_ratio = 0.1)
if((HAS_TRAIT(src, TRAIT_NOHUNGER) || HAS_TRAIT(src, TRAIT_TOXINLOVER)) && !force)
return TRUE
@@ -482,7 +482,6 @@
adjust_nutrition(-lost_nutrition)
adjustToxLoss(-3)
- var/obj/item/organ/stomach/belly = getorganslot(ORGAN_SLOT_STOMACH)
for(var/i=0 to distance)
if(blood)
if(T)
@@ -490,12 +489,8 @@
if(harm)
adjustBruteLoss(3)
else
- if(belly?.reagents.has_reagent(/datum/reagent/consumable/ethanol/blazaam, needs_metabolizing = TRUE))
- if(T)
- T.add_vomit_floor(src, VOMIT_PURPLE)
- else
- if(T)
- T.add_vomit_floor(src, VOMIT_TOXIC, purge) //toxic barf looks different || call purge when doing detoxicfication to pump more chems out of the stomach.
+ if(T)
+ T.add_vomit_floor(src, vomit_type, purge_ratio) //toxic barf looks different || call purge when doing detoxicfication to pump more chems out of the stomach.
T = get_step(T, dir)
if (T?.is_blocked_turf())
break
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 436239ac5b7..6eee57c6208 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -914,7 +914,7 @@
override = dna.species.override_float
..()
-/mob/living/carbon/human/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, toxic = FALSE, harm = TRUE, force = FALSE, purge = FALSE)
+/mob/living/carbon/human/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, vomit_type = VOMIT_TOXIC, harm = TRUE, force = FALSE, purge_ratio = 0.1)
if(blood && (NOBLOOD in dna.species.species_traits) && !HAS_TRAIT(src, TRAIT_TOXINLOVER))
if(message)
visible_message("[src] dry heaves!", \
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 8e17fa6bcf2..85f949bd20a 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -292,7 +292,7 @@
if(getToxLoss() >= 45 && nutrition > 20)
lastpuke += prob(50)
if(lastpuke >= 50) // about 25 second delay I guess
- vomit(20, toxic = TRUE)
+ vomit(20)
lastpuke = 0
diff --git a/code/modules/mob/living/carbon/human/species_types/flypeople.dm b/code/modules/mob/living/carbon/human/species_types/flypeople.dm
index f2f6c114aec..6a59ead83a0 100644
--- a/code/modules/mob/living/carbon/human/species_types/flypeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/flypeople.dm
@@ -71,7 +71,7 @@
if(locate(/datum/reagent/consumable) in reagents.reagent_list)
var/mob/living/carbon/body = owner
// we do not loss any nutrition as a fly when vomiting out food
- body.vomit(0, FALSE, FALSE, 2, TRUE, force=TRUE, purge=TRUE)
+ body.vomit(0, FALSE, FALSE, 2, TRUE, force=TRUE, purge_ratio = 0.67)
playsound(get_turf(owner), 'sound/effects/splat.ogg', 50, TRUE)
body.visible_message("[body] vomits on the floor!", \
"You throw up on the floor!")
diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
index 1cbe372bf01..5f47738756c 100644
--- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
@@ -409,7 +409,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
/datum/reagent/consumable/ethanol/amaretto
name = "Amaretto"
description = "A gentle drink that carries a sweet aroma."
- color = "#E17600"
+ color = "#E17600"
boozepwr = 25
taste_description = "fruity and nutty sweetness"
glass_icon_state = "amarettoglass"
@@ -2222,7 +2222,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
if(prob(10))
stored_teleports += rand(2,6)
if(prob(70))
- M.vomit()
+ M.vomit(vomit_type = VOMIT_PURPLE)
return ..()
@@ -2313,7 +2313,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
glass_icon_state = "godfather"
glass_name = "Godfather"
glass_desc = "A classic from old Italy and enjoyed by gangsters, pray the orange peel doesnt end up in your mouth."
-
+
/datum/reagent/consumable/ethanol/godmother
name = "Godmother"
description = "A twist on a classic, liked more by mature women."
@@ -2324,4 +2324,4 @@ All effects don't start immediately, but rather get worse over time; the rate is
glass_icon_state = "godmother"
glass_name = "Godmother"
glass_desc = "A lovely fresh smelling cocktail, a true Sicilian delight."
-
+
diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
index f672551dcfc..d3407330a2e 100644
--- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
@@ -736,7 +736,7 @@
/datum/reagent/toxin/spewium/on_mob_life(mob/living/carbon/C)
.=..()
if(current_cycle >=11 && prob(min(50,current_cycle)))
- C.vomit(10, prob(10), prob(50), rand(0,4), TRUE, prob(30))
+ C.vomit(10, prob(10), prob(50), rand(0,4), TRUE)
for(var/datum/reagent/toxin/R in C.reagents.reagent_list)
if(R != src)
C.reagents.remove_reagent(R.type,1)
diff --git a/code/modules/research/designs/nanite_designs.dm b/code/modules/research/designs/nanite_designs.dm
index 859bd1f6a3a..c68f473ba92 100644
--- a/code/modules/research/designs/nanite_designs.dm
+++ b/code/modules/research/designs/nanite_designs.dm
@@ -550,16 +550,45 @@
program_type = /datum/nanite_program/protocol/factory
category = list("Protocols_Nanites")
-/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"
- program_type = /datum/nanite_program/protocol/tinker
+/datum/design/nanites/pyramid
+ name = "Pyramid Protocol"
+ desc = "Replication Protocol: the nanites implement an alternate cooperative replication protocol that is more efficient as long as the saturation level is above 80%."
+ id = "pyramid_nanites"
+ program_type = /datum/nanite_program/protocol/pyramid
category = list("Protocols_Nanites")
/datum/design/nanites/offline
- name = "Offline Production Protocol"
+ name = "Eclipse 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"
program_type = /datum/nanite_program/protocol/offline
category = list("Protocols_Nanites")
+
+/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"
+ program_type = /datum/nanite_program/protocol/hive
+ category = list("Protocols_Nanites")
+
+/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"
+ program_type = /datum/nanite_program/protocol/zip
+ category = list("Protocols_Nanites")
+
+/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"
+ program_type = /datum/nanite_program/protocol/free_range
+ category = list("Protocols_Nanites")
+
+/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"
+ program_type = /datum/nanite_program/protocol/unsafe_storage
+ category = list("Protocols_Nanites")
diff --git a/code/modules/research/nanites/nanite_programs/protocols.dm b/code/modules/research/nanites/nanite_programs/protocols.dm
index 89b6acf42aa..9e4161478ff 100644
--- a/code/modules/research/nanites/nanite_programs/protocols.dm
+++ b/code/modules/research/nanites/nanite_programs/protocols.dm
@@ -46,43 +46,25 @@
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 to speed up the replication process."
+/datum/nanite_program/protocol/pyramid
+ name = "Pyramid Protocol"
+ desc = "Replication Protocol: the nanites implement an alternate cooperative replication protocol that is more efficient as long as the saturation level is above 80%."
use_rate = 0
rogue_types = list(/datum/nanite_program/necrotic)
protocol_class = NANITE_PROTOCOL_REPLICATION
- var/boost = 2
- var/list/valid_reagents = list(
- /datum/reagent/iron,
- /datum/reagent/copper,
- /datum/reagent/gold,
- /datum/reagent/silver,
- /datum/reagent/mercury,
- /datum/reagent/aluminium,
- /datum/reagent/silicon)
+ var/boost = 1.2
-/datum/nanite_program/protocol/tinker/check_conditions()
- if(!nanites.host_mob.reagents)
+/datum/nanite_program/protocol/pyramid/check_conditions()
+ if((nanites.nanite_volume / nanites.max_nanites) < 0.8)
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()
+/datum/nanite_program/protocol/pyramid/active_effect()
nanites.adjust_nanites(null, boost)
/datum/nanite_program/protocol/offline
- name = "Offline Production Protocol"
+ name = "Eclipse 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)
@@ -98,3 +80,195 @@
/datum/nanite_program/protocol/offline/active_effect()
nanites.adjust_nanites(null, boost)
+
+/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 in a host."
+ use_rate = 0
+ rogue_types = list(/datum/nanite_program/necrotic)
+ protocol_class = NANITE_PROTOCOL_STORAGE
+ 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, greatly increasing the maximum volume while in a host. However, the process slows down the replication rate slightly."
+ use_rate = 0.2
+ rogue_types = list(/datum/nanite_program/necrotic)
+ protocol_class = NANITE_PROTOCOL_STORAGE
+ 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, but increases the replication rate."
+ use_rate = 0
+ rogue_types = list(/datum/nanite_program/necrotic)
+ protocol_class = NANITE_PROTOCOL_STORAGE
+ var/boost = 0.5
+ 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/free_range/active_effect()
+ nanites.adjust_nanites(null, boost)
+
+/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 much higher saturation levels, but at the risk of causing internal damage to the host."
+ use_rate = 0
+ rogue_types = list(/datum/nanite_program/necrotic)
+ protocol_class = NANITE_PROTOCOL_STORAGE
+ var/extra_volume = 1500
+ var/next_warning = 0
+ var/min_warning_cooldown = 120
+ var/max_warning_cooldown = 350
+ var/volume_warnings_stage_1 = list("You feel a dull pain in your abdomen.",
+ "You feel a tickling sensation in your abdomen.")
+ var/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/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/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/volume_warnings_stage_5 = list("You feel sick.",
+ "You feel a dull pain from every part of your body.",
+ "You feel nauseous.")
+ var/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(prob(10))
+ host_mob.adjustBruteLoss(((max(nanites.nanite_volume - 450, 0) / 450) ** 2 ) * 0.5) // 0.5 -> 2 -> 4.5 -> 8 damage per successful tick
+ return
+
+ var/mob/living/carbon/C = host_mob
+
+ if(nanites.nanite_volume < 500)
+ return
+
+ var/current_stage = 0
+ 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))
+ var/obj/item/organ/liver/liver = C.getorganslot(ORGAN_SLOT_LIVER)
+ if(liver)
+ liver.applyOrganDamage(0.6)
+ current_stage++
+ if(nanites.nanite_volume > 750) //Extra volume spills out in other central organs
+ if(prob(10))
+ var/obj/item/organ/stomach/stomach = C.getorganslot(ORGAN_SLOT_STOMACH)
+ if(stomach)
+ stomach.applyOrganDamage(0.75)
+ if(prob(10))
+ var/obj/item/organ/lungs/lungs = C.getorganslot(ORGAN_SLOT_LUNGS)
+ if(lungs)
+ lungs.applyOrganDamage(0.75)
+ current_stage++
+ if(nanites.nanite_volume > 1000) //Extra volume spills out in more critical organs
+ if(prob(10))
+ var/obj/item/organ/heart/heart = C.getorganslot(ORGAN_SLOT_HEART)
+ if(heart)
+ heart.applyOrganDamage(0.75)
+ if(prob(10))
+ var/obj/item/organ/brain/brain = C.getorganslot(ORGAN_SLOT_BRAIN)
+ if(brain)
+ brain.applyOrganDamage(0.75)
+ current_stage++
+ if(nanites.nanite_volume > 1250) //Excess nanites start invading smaller organs for more space, including sensory organs
+ if(prob(13))
+ var/obj/item/organ/eyes/eyes = C.getorganslot(ORGAN_SLOT_EYES)
+ if(eyes)
+ eyes.applyOrganDamage(0.75)
+ if(prob(13))
+ var/obj/item/organ/ears/ears = C.getorganslot(ORGAN_SLOT_EARS)
+ if(ears)
+ ears.applyOrganDamage(0.75)
+ current_stage++
+ if(nanites.nanite_volume > 1500) //Nanites start spilling into the bloodstream, causing toxicity
+ if(prob(15))
+ C.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))
+ C.adjustBruteLoss(0.75, TRUE)
+ current_stage++
+
+ volume_warning(current_stage)
+
+/datum/nanite_program/protocol/unsafe_storage/proc/volume_warning(tier)
+ if(world.time < next_warning)
+ return
+
+ var/list/main_warnings
+ var/list/extra_warnings
+
+ switch(tier)
+ if(1)
+ main_warnings = volume_warnings_stage_1
+ extra_warnings = null
+ if(2)
+ main_warnings = volume_warnings_stage_2
+ extra_warnings = volume_warnings_stage_1
+ if(3)
+ main_warnings = volume_warnings_stage_3
+ extra_warnings = volume_warnings_stage_1 + volume_warnings_stage_2
+ if(4)
+ main_warnings = volume_warnings_stage_4
+ extra_warnings = volume_warnings_stage_1 + volume_warnings_stage_2 + volume_warnings_stage_3
+ if(5)
+ main_warnings = volume_warnings_stage_5
+ extra_warnings = volume_warnings_stage_1 + volume_warnings_stage_2 + volume_warnings_stage_3 + volume_warnings_stage_4
+ if(6)
+ main_warnings = volume_warnings_stage_6
+ extra_warnings = 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, "[pick(main_warnings)]")
+ next_warning = world.time + rand(min_warning_cooldown, max_warning_cooldown)
+ else if(islist(extra_warnings))
+ to_chat(host_mob, "[pick(extra_warnings)]")
+ next_warning = world.time + rand(min_warning_cooldown, max_warning_cooldown)
diff --git a/code/modules/research/nanites/nanite_programs/weapon.dm b/code/modules/research/nanites/nanite_programs/weapon.dm
index 4f7acec3dfa..259b85a4875 100644
--- a/code/modules/research/nanites/nanite_programs/weapon.dm
+++ b/code/modules/research/nanites/nanite_programs/weapon.dm
@@ -88,11 +88,7 @@
addtimer(CALLBACK(src, .proc/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)
+ dyn_explosion(get_turf(host_mob), nanites.nanite_volume / 50)
qdel(nanites)
//TODO make it defuse if triggered again
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 6478b8feeae..6d831fecc0a 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -900,9 +900,19 @@
/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."
+ description = "Protocols that overwrite the default nanite replication routine to achieve more efficiency in certain circumstances."
prereq_ids = list("nanite_smart")
- design_ids = list("kickstart_nanites","factory_nanites","tinker_nanites","offline_nanites")
+ design_ids = list("kickstart_nanites","factory_nanites","pyramid_nanites","offline_nanites")
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000, 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("hive_nanites","zip_nanites","free_range_nanites","unsafe_storage_nanites")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000, TECHWEB_POINT_TYPE_NANITES = 2500)
hidden = TRUE
experimental = TRUE
diff --git a/code/modules/surgery/stomachpump.dm b/code/modules/surgery/stomachpump.dm
index a07f45a9f4c..a302621fb53 100644
--- a/code/modules/surgery/stomachpump.dm
+++ b/code/modules/surgery/stomachpump.dm
@@ -39,7 +39,7 @@
display_results(user, target, "[user] forces [H] to vomit, cleansing their stomach of some chemicals!",
"[user] forces [H] to vomit, cleansing their stomach of some chemicals!",
"[user] forces [H] to vomit!")
- H.vomit(20, FALSE, TRUE, 1, TRUE, FALSE, purge = TRUE) //called with purge as true to lose more reagents
+ H.vomit(20, FALSE, TRUE, 1, TRUE, FALSE, purge_ratio = 0.67) //higher purge ratio than regular vomiting
return ..()
/datum/surgery_step/stomach_pump/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
diff --git a/icons/effects/blood.dmi b/icons/effects/blood.dmi
index d16dcf7c30b..bee16d4d908 100644
Binary files a/icons/effects/blood.dmi and b/icons/effects/blood.dmi differ