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