From 58ee6891f2b668293c7882bac295312da33b3a21 Mon Sep 17 00:00:00 2001 From: Arokha Sieyes Date: Sat, 3 Jun 2017 00:55:01 -0400 Subject: [PATCH] Additional Work on NIFs Squashed commits: [9563a9a] All the stuff I forgot to commit --- code/ZAS/Phoron.dm | 4 + code/__defines/nifsoft.dm | 103 ++++ code/game/objects/items/devices/flash.dm | 10 +- code/game/objects/items/weapons/tools.dm | 1 + code/modules/mob/living/carbon/breathe.dm | 7 + code/modules/mob/living/carbon/carbon.dm | 1 + .../mob/living/carbon/human/human_organs.dm | 3 +- code/modules/mob/living/carbon/human/life.dm | 3 +- .../mob/living/carbon/human/life_vr.dm | 3 +- code/modules/nifsoft/nif.dm | 481 ++++++++++++++++++ code/modules/nifsoft/nif_softshop.dm | 111 ++++ code/modules/nifsoft/nif_statpanel.dm | 77 +++ code/modules/nifsoft/nifsoft.dm | 110 ++++ code/modules/nifsoft/software/01_vision.dm | 253 +++++++++ code/modules/nifsoft/software/05_health.dm | 184 +++++++ code/modules/nifsoft/software/10_combat.dm | 7 + code/modules/nifsoft/software/15_misc.dm | 9 + code/modules/organs/organ_external.dm | 2 +- icons/obj/device_alt.dmi | Bin 54630 -> 54974 bytes icons/obj/vending_vr.dmi | Bin 67287 -> 67288 bytes vorestation.dme | 6 +- 21 files changed, 1368 insertions(+), 7 deletions(-) create mode 100644 code/__defines/nifsoft.dm create mode 100644 code/modules/nifsoft/nif.dm create mode 100644 code/modules/nifsoft/nif_softshop.dm create mode 100644 code/modules/nifsoft/nif_statpanel.dm create mode 100644 code/modules/nifsoft/nifsoft.dm create mode 100644 code/modules/nifsoft/software/01_vision.dm create mode 100644 code/modules/nifsoft/software/05_health.dm create mode 100644 code/modules/nifsoft/software/10_combat.dm create mode 100644 code/modules/nifsoft/software/15_misc.dm diff --git a/code/ZAS/Phoron.dm b/code/ZAS/Phoron.dm index acee6a8b65b..1f7ed8c04a1 100644 --- a/code/ZAS/Phoron.dm +++ b/code/ZAS/Phoron.dm @@ -117,6 +117,10 @@ obj/var/phoronproof = 0 if(burn_eyes && head && (head.body_parts_covered & EYES) && (head.item_flags & AIRTIGHT)) burn_eyes = 0 + //VOREStation Edit - NIF Support + if(nif && nif.flag_check(NIF_V_UVFILTER,NIF_FLAGS_VISION)) + burn_eyes = 0 + //If we still need to, burn their eyes if(burn_eyes) burn_eyes() diff --git a/code/__defines/nifsoft.dm b/code/__defines/nifsoft.dm new file mode 100644 index 00000000000..e4d4f122932 --- /dev/null +++ b/code/__defines/nifsoft.dm @@ -0,0 +1,103 @@ +// List indexes for software datum references on mobs +// This also controls the order they are displayed in the NIF stat panel +//AR Overlays +#define NIF_CIVILIAN_AR 1 +#define NIF_MEDICAL_AR 2 +#define NIF_SECURITY_AR 3 +#define NIF_ENGINE_AR 4 +#define NIF_SCIENCE_AR 5 +#define NIF_OMNI_AR 6 +//Misc Vision +#define NIF_CORRECTIVE_GLASS 7 +#define NIF_MESONS 8 +#define NIF_MATERIAL 9 +#define NIF_THERMALS 10 +#define NIF_NIGHTVIS 11 +#define NIF_UVFILTER 12 +#define NIF_FLASHPROT 13 +//Health-related +#define NIF_ORGANIC_HEAL 14 +#define NIF_SYNTH_HEAL 15 +#define NIF_AUTOSTASIS 16 //These two are just part of +#define NIF_MED_ALARM 17 //medichines right now +#define NIF_TOXHEAL 18 +#define NIF_SPAREBREATH 19 +//Combat Related +#define NIF_BRUTEARMOR 20 +#define NIF_BURNARMOR 21 +#define NIF_PAINKILLERS 22 +#define NIF_HARDCLAWS 23 +#define NIF_HIDDENLASER 24 +//Other +#define NIF_COMMLINK 25 +#define NIF_SUITSENSORS 26 +#define NIF_APCCHARGE 27 +#define NIF_PRESSURE 28 +#define NIF_HEATSINK 29 + +// Must be equal to the highest number above +#define TOTAL_NIF_SOFTWARE 29 + +////////////////////// +// NIF flag list hints +#define NIF_FLAGS_VISION 1 +#define NIF_FLAGS_HEALTH 2 +#define NIF_FLAGS_COMBAT 3 +#define NIF_FLAGS_OTHER 4 + +// NIF flags +//Vision +#define NIF_V_AR_CIVILIAN 0x1 +#define NIF_V_AR_MEDICAL 0x2 +#define NIF_V_AR_SECURITY 0x4 +#define NIF_V_AR_ENGINE 0x8 +#define NIF_V_AR_SCIENCE 0x10 +#define NIF_V_AR_OMNI 0x20 +#define NIF_V_CORRECTIVE 0x40 +#define NIF_V_MESONS 0x80 +#define NIF_V_MATERIAL 0x100 +#define NIF_V_THERMALS 0x200 +#define NIF_V_NIGHTVIS 0x400 +#define NIF_V_UVFILTER 0x800 +#define NIF_V_FLASHPROT 0x1000 + +//Health +#define NIF_H_ORGREPAIR 0x1 +#define NIF_H_SYNTHREPAIR 0x2 +#define NIF_H_AUTOSTASIS 0x4 //These two are just part of +#define NIF_H_ALERTMED 0x8 //medichines right now +#define NIF_H_TOXREGEN 0x10 +#define NIF_H_SPAREBREATH 0x20 + +//Combat +#define NIF_C_BRUTEARMOR 0x1 +#define NIF_C_BURNARMOR 0x2 +#define NIF_C_PAINKILLERS 0x4 +#define NIF_C_HARDCLAWS 0x8 +#define NIF_C_HIDELASER 0x10 + +//Other +#define NIF_O_COMMLINK 0x1 +#define NIF_O_SENSORS 0x2 +#define NIF_O_APCCHARGE 0x4 +#define NIF_O_PRESSURESEAL 0x8 +#define NIF_O_HEATSINKS 0x10 + +/////////////////// +// applies_to flags +#define NIF_ORGANIC 0x1 +#define NIF_SYNTHETIC 0x2 + +///////////// +// stat flags +#define NIF_WORKING 0 +#define NIF_POWFAIL 1 +#define NIF_TEMPFAIL 2 +#define NIF_INSTALLING 3 +#define NIF_PREINSTALL 4 + +/////////////////// +// tick_flags flags +#define NIF_NEVERTICK 0 +#define NIF_ALWAYSTICK 1 +#define NIF_ACTIVETICK 2 diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index 632f9afdfd9..758678bed97 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -81,7 +81,15 @@ playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) var/flashfail = 0 - if(iscarbon(M)) + //VOREStation Add - NIF + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.nif && H.nif.flag_check(NIF_V_FLASHPROT,NIF_FLAGS_VISION)) + flashfail = 1 + H.nif.notify("High intensity light detected, and blocked!",TRUE) + //VOREStation Add End + + if(iscarbon(M) && !flashfail) //VOREStation Add - NIF var/mob/living/carbon/C = M if(C.stat != DEAD) var/safety = C.eyecheck() diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm index 8c4067187fe..555488cd898 100644 --- a/code/game/objects/items/weapons/tools.dm +++ b/code/game/objects/items/weapons/tools.dm @@ -376,6 +376,7 @@ var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] if(!E) return + if(H.nif && H.nif.flag_check(NIF_V_UVFILTER,NIF_FLAGS_VISION)) return //VOREStation Add - NIF switch(safety) if(1) usr << "Your eyes sting a little." diff --git a/code/modules/mob/living/carbon/breathe.dm b/code/modules/mob/living/carbon/breathe.dm index 354a26221c4..403088dc4bd 100644 --- a/code/modules/mob/living/carbon/breathe.dm +++ b/code/modules/mob/living/carbon/breathe.dm @@ -22,6 +22,13 @@ else //Okay, we can breathe, now check if we can get air breath = get_breath_from_internal() //First, check for air from internals + //VOREStation Add - Respirocytes as a NIF implant + if(ishuman(src)) + var/mob/living/carbon/human/H = src + if(H.nif && H.nif.flag_check(NIF_H_SPAREBREATH,NIF_FLAGS_HEALTH)) + var/datum/nifsoft/spare_breath/SB = H.nif.imp_check(NIF_SPAREBREATH) + breath = SB.resp_breath() + //VOREStation Add End if(!breath) breath = get_breath_from_environment() //No breath from internals so let's try to get air from our location if(!breath) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 47da8c88f8a..47e12f3f6da 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -254,6 +254,7 @@ src.sleeping = max(0,src.sleeping-5) if(src.sleeping == 0) src.resting = 0 + if(H) H.in_stasis = 0 //VOREStation Add - Just In Case M.visible_message("[M] shakes [src] trying to wake [t_him] up!", \ "You shake [src] trying to wake [t_him] up!") else diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm index 4fa6ae11176..4331b05407e 100644 --- a/code/modules/mob/living/carbon/human/human_organs.dm +++ b/code/modules/mob/living/carbon/human/human_organs.dm @@ -32,7 +32,7 @@ if(force_process) bad_external_organs.Cut() for(var/obj/item/organ/external/Ex in organs) - bad_external_organs |= Ex + bad_external_organs += Ex //VOREStation Edit - Silly and slow to |= this //processing internal organs is pretty cheap, do that first. for(var/obj/item/organ/I in internal_organs) @@ -44,6 +44,7 @@ if(!force_process && !bad_external_organs.len) return + number_wounds = 0 //VOREStation Add - You have to reduce this at some point... for(var/obj/item/organ/external/E in bad_external_organs) if(!E) continue diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 753cf3255e7..75d598fb696 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -1267,7 +1267,7 @@ var/obj/item/clothing/glasses/G = glasses if(!G.prescription) set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) - else + else if (!nif || !nif.flag_check(NIF_V_CORRECTIVE,NIF_FLAGS_VISION)) //VOREStation Edit - NIF set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) @@ -1282,6 +1282,7 @@ var/obj/item/clothing/glasses/welding/O = glasses if(!O.up) found_welder = 1 + if(!found_welder && nif && nif.flag_check(NIF_V_UVFILTER,NIF_FLAGS_VISION)) found_welder = 1 //VOREStation Add - NIF if(!found_welder && istype(head, /obj/item/clothing/head/welding)) var/obj/item/clothing/head/welding/O = head if(!O.up) diff --git a/code/modules/mob/living/carbon/human/life_vr.dm b/code/modules/mob/living/carbon/human/life_vr.dm index a04232c401a..0a3d2fcb3fb 100644 --- a/code/modules/mob/living/carbon/human/life_vr.dm +++ b/code/modules/mob/living/carbon/human/life_vr.dm @@ -44,8 +44,9 @@ else vantag.icon_state = "hudblank" +//Our call for the NIF to do whatever /mob/living/carbon/human/proc/handle_nif() if(!nif) return //Process regular life stuff - nif.life() \ No newline at end of file + nif.life() diff --git a/code/modules/nifsoft/nif.dm b/code/modules/nifsoft/nif.dm new file mode 100644 index 00000000000..b4ef9eafd70 --- /dev/null +++ b/code/modules/nifsoft/nif.dm @@ -0,0 +1,481 @@ +//Holder on humans to prevent having to 'find' it every time +/mob/living/carbon/human/var/obj/item/device/nif/nif + +//Nanotech Implant Foundation +/obj/item/device/nif + name = "nanotech implant foundation" + desc = "A somewhat degraded copy of a Kitsuhana working surface, in a box. Can print new \ + implants inside living hosts on the fly based on software uploads. Must be surgically \ + implanted in the head to work. May eventually wear out and break." + + icon = 'icons/obj/device_alt.dmi' + icon_state = "nif_0" + + w_class = ITEMSIZE_TINY + + var/durability = 100 // Durability remaining + var/burn_factor = 100 // Divisor for power charge from nutrition (efficiency basically) + + var/tmp/power_usage = 0 // Nifsoft adds to this + var/tmp/mob/living/carbon/human/human // Our owner! + var/tmp/list/nifsofts[TOTAL_NIF_SOFTWARE] // All our nifsofts + var/tmp/list/nifsofts_life = list() // Ones that want to be talked to on life() + var/owner // Owner character name + + var/tmp/vision_flags = 0 // Flags implants set for faster lookups + var/tmp/health_flags = 0 + var/tmp/combat_flags = 0 + var/tmp/other_flags = 0 + + var/tmp/stat = NIF_PREINSTALL // Status of the NIF + var/tmp/install_done // Time when install will finish + var/tmp/open = FALSE // If it's open for maintenance (1-3) + + var/obj/item/clothing/glasses/hud/nif_hud/nif_hud + + var/global/icon/big_icon + var/global/click_sound = 'sound/effects/pop.ogg' + +//Constructor comes with a free AR HUD +/obj/item/device/nif/New(var/newloc,var/wear) + ..(newloc) + new /datum/nifsoft/ar_civ(src) + nif_hud = new(src) + + if(!big_icon) + big_icon = new(icon,icon_state = "nif_full") + + //Probably loading from a save + if(ishuman(newloc)) + var/mob/living/carbon/human/H = newloc + implant(H) + owner = H.mind.name + name = initial(name) + " ([owner])" + stat = NIF_WORKING + + if(wear) + durability = wear + + update_icon() + +//Destructor cleans up references +/obj/item/device/nif/Destroy() + if(human) + human.nif = null + human = null + for(var/S in nifsofts) + if(S) + qdel(S) + nifsofts.Cut() + ..() + +//Being implanted in some mob +/obj/item/device/nif/proc/implant(var/mob/living/carbon/human/H) + if(istype(H) && !H.nif && H.species && !(H.species.flags & NO_SCAN) && (loc == H.get_organ(BP_HEAD))) //NO_SCAN is the default 'too complicated' flag. + human = H + human.nif = src + stat = NIF_INSTALLING + return TRUE + + return FALSE + +//For debug or antag purposes +/obj/item/device/nif/proc/quick_implant(var/mob/living/carbon/human/H) + if(istype(H)) + var/obj/item/organ/external/head = H.get_organ(BP_HEAD) + if(!head) + return FALSE + src.forceMove(head) + head.implants += src + owner = H.real_name + return implant(H) + + return FALSE + +//Being implanted in some mob +/obj/item/device/nif/proc/unimplant(var/mob/living/carbon/human/H) + human = null + stat = NIF_PREINSTALL + install_done = null + if(istype(H)) + H.nif = null + update_icon() + +//EMP adds wear and disables all nifsoft +/obj/item/device/nif/emp_act(var/severity) + notify("Danger! Significant electromagnetic interference!",TRUE) + for(var/nifsoft in nifsofts) + if(nifsoft) + var/datum/nifsoft/NS = nifsoft + NS.deactivate() + + switch (severity) + if (1) + wear(rand(30,40)) + if (2) + wear(rand(15,25)) + if (3) + wear(rand(8,15)) + if (4) + wear(rand(1,8)) + +//Wear update/check proc +/obj/item/device/nif/proc/wear(var/wear = 0) + if(wear) + durability -= wear * rand(0.85,1.15) // Also +/- 15% + + if(durability <= 0) + stat = NIF_TEMPFAIL + update_icon() + notify("Danger! General system insta#^!($",TRUE) + to_chat(human,"Your NIF vision overlays disappear and your head suddenly seems very quiet...") + +//Attackby proc, for maintenance +/obj/item/device/nif/attackby(obj/item/weapon/W, mob/user as mob) + if(open == 0 && istype(W,/obj/item/weapon/screwdriver)) + if(do_after(user, 4 SECONDS, src) && open == 0) + user.visible_message("[user] unscrews and pries open \the [src].","You unscrew and pry open \the [src].") + playsound(user, 'sound/items/Screwdriver.ogg', 50, 1) + open = 1 + update_icon() + else if(open == 1 && istype(W,/obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = W + if(C.get_amount() < 3) + to_chat(user,"You need at least three coils of wire to add them to \the [src].") + return + if(do_after(user, 6 SECONDS, src) && open == 1 && C.use(3)) + user.visible_message("[user] replaces some wiring in \the [src].","You replace any burned out wiring in \the [src].") + playsound(user, 'sound/items/Deconstruct.ogg', 50, 1) + open = 2 + update_icon() + else if(open == 2 && istype(W,/obj/item/device/multitool)) + if(do_after(user, 8 SECONDS, src) && open == 2) + user.visible_message("[user] resets several circuits in \the [src].","You find and repair any faulty circuits in \the [src].") + open = 3 + update_icon() + else if(open == 3 && istype(W,/obj/item/weapon/screwdriver)) + if(do_after(user, 3 SECONDS, src) && open == 3) + user.visible_message("[user] closes up \the [src].","You re-seal \the [src] for use once more.") + playsound(user, 'sound/items/Screwdriver.ogg', 50, 1) + open = FALSE + durability = initial(durability) + stat = NIF_PREINSTALL + update_icon() + +//Wear update/check proc +/obj/item/device/nif/update_icon() + if(open) + icon_state = "nif_open[open]" + else + switch(stat) + if(NIF_PREINSTALL) + icon_state = "nif_1" + if(NIF_INSTALLING) + icon_state = "nif_0" + if(NIF_WORKING) + icon_state = "nif_0" + if(NIF_TEMPFAIL) + icon_state = "nif_2" + else + icon_state = "nif_2" + +//The (dramatic) install process +/obj/item/device/nif/proc/handle_install() + if(human.stat) //No stuff while KO. Sleeping it off is viable, and doesn't start until you wake up from surgery + return FALSE + + //Firsties + if(!install_done) + if(human.real_name == owner) + install_done = world.time + 1 MINUTE + notify("Welcome back, [owner]! Performing quick-calibration...") + else + install_done = world.time + 30 MINUTES + notify("Adapting to new user...") + sleep(5 SECONDS) + notify("Adjoining optic [human.isSynthetic() ? "interface" : "nerve"], please be patient.",TRUE) + + var/percent_done = (world.time - (install_done - (30 MINUTES))) / (30 MINUTES) + + human.client.screen.Add(global_hud.whitense) + + switch(percent_done) //This is 0.0 to 1.0 kinda percent. + //Connecting to optical nerves + if(0.0 to 0.1) + human.eye_blind = 5 + + //Mapping brain + if(0.2 to 0.9) + if(prob(99)) return TRUE + var/incident = rand(1,3) + switch(incident) + if(1) + var/message = pick(list( + "Your head throbs around your new implant.", + "The skin around your recent surgery itches.", + "A wave of nausea overtakes you as the world seems to spin.", + "The floor suddenly seems to come up at you.", + "There's a throbbing lump of ice behind your eyes.", + "A wave of pain shoots down your neck." + )) + to_chat(human,"[message]") + if(2) + human.Weaken(5) + to_chat(human,"A wave of weakness rolls over you.") + if(3) + human.Sleeping(5) + to_chat(human,"You suddenly black out!") + + //Finishing up + if(1.0 to INFINITY) + stat = NIF_WORKING + owner = human.real_name + name = initial(name) + " ([owner])" + notify("Calibration complete! User data stored!") + +//Called each life() tick on the mob +/obj/item/device/nif/proc/life() + if(!human || loc != human.get_organ(BP_HEAD)) + unimplant(human) + return FALSE + + switch(stat) + if(NIF_WORKING) + //Perform our passive drain + if(!use_charge(power_usage)) + stat = NIF_POWFAIL + notify("Insufficient energy!",TRUE) + return FALSE + + //HUD update! + nif_hud.process_hud(human,1) + + //Process all the ones that want that + for(var/S in nifsofts_life) + var/datum/nifsoft/nifsoft = S + nifsoft.life(human) + + if(NIF_POWFAIL) + if(human && human.nutrition < 100) + return FALSE + else + stat = NIF_WORKING + notify("System Reboot Complete.") + + if(NIF_TEMPFAIL) + //Something else has to take us out of tempfail + return FALSE + + if(NIF_INSTALLING) + handle_install() + return FALSE + +//Prints 'AR' messages to the user +/obj/item/device/nif/proc/notify(var/message,var/alert = 0) + if(!human) return + + to_chat(human,"\[\icon[src.big_icon]NIF\] displays, \"[message]\"") + +//Called to spend nutrition, returns 1 if it was able to +/obj/item/device/nif/proc/use_charge(var/use_charge) + //You don't want us to take any? Well okay. + if(!use_charge) + return TRUE + + //Not enough nutrition/charge left. + if(!human || human.nutrition < use_charge) + return FALSE + + //Was enough, reduce and return. + human.nutrition -= use_charge + return TRUE + +//Install a piece of software +/obj/item/device/nif/proc/install(var/datum/nifsoft/new_soft) + if(nifsofts[new_soft.list_pos]) + return FALSE + + if(human) + var/applies_to = new_soft.applies_to + var/synth = human.isSynthetic() + if(synth && !(applies_to & NIF_SYNTHETIC)) + notify("The software \"[new_soft]\" is not supported on your chassis type.",TRUE) + return FALSE + if(!synth && !(applies_to & NIF_ORGANIC)) + notify("The software \"[new_soft]\" is not supported in organic life.",TRUE) + return FALSE + + nifsofts[new_soft.list_pos] = new_soft + power_usage += new_soft.p_drain + + if(new_soft.tick_flags == NIF_ALWAYSTICK) + nifsofts_life += new_soft + + if(new_soft.vision_flags) + vision_flags |= new_soft.vision_flags + if(new_soft.health_flags) + health_flags |= new_soft.health_flags + if(new_soft.combat_flags) + combat_flags |= new_soft.combat_flags + if(new_soft.other_flags) + other_flags |= new_soft.other_flags + + wear(new_soft.wear) + return TRUE + +//Uninstall a piece of software +/obj/item/device/nif/proc/uninstall(var/datum/nifsoft/old_soft) + var/datum/nifsoft/NS = old_soft.list_pos + if(!NS || NS != old_soft) + return FALSE //what?? + + nifsofts[old_soft.list_pos] = null + power_usage -= old_soft.p_drain + + if(old_soft.tick_flags == NIF_ALWAYSTICK) + nifsofts_life -= old_soft + + if(old_soft.active) + power_usage -= old_soft.a_drain + if(old_soft.tick_flags == NIF_ACTIVETICK) + nifsofts_life -= old_soft + + if(old_soft.vision_flags) + vision_flags &= ~old_soft.vision_flags + if(old_soft.health_flags) + health_flags &= ~old_soft.health_flags + if(old_soft.combat_flags) + combat_flags &= ~old_soft.combat_flags + if(old_soft.other_flags) + other_flags &= ~old_soft.other_flags + + return TRUE + +//Activate a nifsoft +/obj/item/device/nif/proc/activate(var/datum/nifsoft/soft) + if(human) + var/applies_to = soft.applies_to + var/synth = human.isSynthetic() + if(synth && !(applies_to & NIF_SYNTHETIC)) + notify("The software \"[soft]\" is not supported on your chassis type and will be uninstalled.",TRUE) + uninstall(soft) + return FALSE + if(!synth && !(applies_to & NIF_ORGANIC)) + notify("The software \"[soft]\" is not supported in organic life and will be uninstalled.",TRUE) + uninstall(soft) + return FALSE + + if(!use_charge(soft.a_drain)) + return FALSE + + if(soft.tick_flags == NIF_ACTIVETICK) + nifsofts_life += soft + + power_usage += soft.a_drain + human << click_sound + + return TRUE + +//Deactivate a nifsoft +/obj/item/device/nif/proc/deactivate(var/datum/nifsoft/soft) + if(soft.tick_flags == NIF_ACTIVETICK) + nifsofts_life -= soft + + power_usage -= soft.a_drain + human << click_sound + + return TRUE + +//Deactivate several nifsofts +/obj/item/device/nif/proc/deactivate_these(var/list/turn_off) + for(var/N in turn_off) + var/datum/nifsoft/NS = nifsofts[N] + if(NS) + NS.deactivate() + +//Add a flag to one of the holders +/obj/item/device/nif/proc/set_flag(var/flag,var/hint) + ASSERT(flag && hint) + + switch(hint) + if(NIF_FLAGS_VISION) + vision_flags |= flag + if(NIF_FLAGS_HEALTH) + health_flags |= flag + if(NIF_FLAGS_COMBAT) + combat_flags |= flag + if(NIF_FLAGS_OTHER) + other_flags |= flag + else + CRASH("Not a valid NIF flag hint: [hint]") + +//Clear a flag from one of the holders +/obj/item/device/nif/proc/clear_flag(var/flag,var/hint) + ASSERT(flag && hint) + + switch(hint) + if(NIF_FLAGS_VISION) + vision_flags &= ~flag + if(NIF_FLAGS_HEALTH) + health_flags &= ~flag + if(NIF_FLAGS_COMBAT) + combat_flags &= ~flag + if(NIF_FLAGS_OTHER) + other_flags &= ~flag + else + CRASH("Not a valid NIF flag hint: [hint]") + +//Check for an installed implant +/obj/item/device/nif/proc/imp_check(var/soft) + if(!stat == NIF_WORKING) return FALSE + ASSERT(soft) + + if(ispath(soft)) + var/datum/nifsoft/path = soft + soft = initial(path.list_pos) + var/entry = nifsofts[soft] + if(entry) + return entry + +//Check for a set flag +/obj/item/device/nif/proc/flag_check(var/flag,var/hint) + if(!stat == NIF_WORKING) return FALSE + ASSERT(flag && hint) + + var/result = FALSE + switch(hint) + if(NIF_FLAGS_VISION) + if(flag & vision_flags) result = TRUE + if(NIF_FLAGS_HEALTH) + if(flag & health_flags) result = TRUE + if(NIF_FLAGS_COMBAT) + if(flag & combat_flags) result = TRUE + if(NIF_FLAGS_OTHER) + if(flag & other_flags) result = TRUE + else + CRASH("Not a valid NIF flag hint: [hint]") + + return result + +//NIF HUD object becasue HUD handling is trash and should be rewritten +/obj/item/clothing/glasses/hud/nif_hud/var/obj/item/device/nif/nif + +/obj/item/clothing/glasses/hud/nif_hud/New(var/newloc) + ..(newloc) + nif = newloc + +/obj/item/clothing/glasses/hud/nif_hud/process_hud(M,var/thing) + //Faster checking with local var, and this is called often so I want fast. + var/visflags = nif.vision_flags + if(NIF_V_AR_OMNI & visflags) + process_omni_hud(nif.human, "best") + else if(NIF_V_AR_SECURITY & visflags) + process_omni_hud(nif.human, "sec") + else if(NIF_V_AR_MEDICAL & visflags) + process_omni_hud(nif.human, "med") + else if(NIF_V_AR_ENGINE & visflags) + process_omni_hud(nif.human, "eng") + else if(NIF_V_AR_SCIENCE & visflags) + process_omni_hud(nif.human, "sci") + else if(NIF_V_AR_CIVILIAN & visflags) + process_omni_hud(nif.human, "civ") diff --git a/code/modules/nifsoft/nif_softshop.dm b/code/modules/nifsoft/nif_softshop.dm new file mode 100644 index 00000000000..3680b47786a --- /dev/null +++ b/code/modules/nifsoft/nif_softshop.dm @@ -0,0 +1,111 @@ +//Custom vendors +/obj/machinery/vending/nifsoft_shop + name = "NIFSoft Shop" + desc = "For all your mindware and mindware accessories." + product_ads = "Let us get into your head!;Looking for an upgrade?;Surpass Humanity!;Why be normal when you can be SUPERnormal?;Jack in with NIFSoft!" + icon_state = "nifsoft" + icon_vend = "nifsoft-purchase" + icon_deny = "nifsoft-problem" + products = list() + contraband = list() + premium = list() + var/global/list/starting_legal_nifsoft + var/global/list/starting_illegal_nifsoft + +// Special Treatment! +/obj/machinery/vending/nifsoft_shop/build_inventory() + //Firsties + if(!starting_legal_nifsoft) + starting_legal_nifsoft = list() + starting_illegal_nifsoft = list() + for(var/P in subtypesof(/datum/nifsoft) - /datum/nifsoft/package) + var/datum/nifsoft/NS = P + if(initial(NS.initial)) + switch(initial(NS.illegal)) + if(TRUE) + starting_illegal_nifsoft += NS + if(FALSE) + starting_legal_nifsoft += NS + + products = starting_legal_nifsoft.Copy() + contraband = starting_illegal_nifsoft.Copy() + + var/list/all_products = list( + list(products, CAT_NORMAL), + list(contraband, CAT_HIDDEN), + list(premium, CAT_COIN)) + + for(var/current_list in all_products) + var/category = current_list[2] + + for(var/entry in current_list[1]) + var/datum/nifsoft/NS = entry + var/name = initial(NS.name) + var/datum/stored_item/vending_product/product = new/datum/stored_item/vending_product(src, entry, name) + + product.price = initial(NS.cost) + product.amount = 10 + product.category = category + + product_records.Add(product) + +/obj/machinery/vending/nifsoft_shop/allowed(mob/user) + if(!ishuman(user)) + return FALSE + + var/mob/living/carbon/human/H = user + if(!H.nif || !H.nif.stat == NIF_WORKING) + to_chat(H,"[src] seems unable to connect to your NIF...") + flick(icon_deny,src) + return FALSE + + return ..() + +// Also special treatment! +/obj/machinery/vending/nifsoft_shop/vend(datum/stored_item/vending_product/R, mob/user) + var/mob/living/carbon/human/H = user + if((!allowed(usr)) && !emagged && scan_id && istype(H)) //For SECURE VENDING MACHINES YEAH + usr << "Purchase not allowed." //Unless emagged of course + flick(icon_deny,src) + return + vend_ready = 0 //One thing at a time!! + status_message = "Installing..." + status_error = 0 + nanomanager.update_uis(src) + + if(R.category & CAT_COIN) + if(!coin) + user << "You need to insert a coin to get this item." + return + if(coin.string_attached) + if(prob(50)) + user << "You successfully pull the coin out before \the [src] could swallow it." + else + user << "You weren't able to pull the coin out fast enough, the machine ate it, string and all." + qdel(coin) + coin = null + categories &= ~CAT_COIN + else + qdel(coin) + coin = null + categories &= ~CAT_COIN + + if(((last_reply + (vend_delay + 200)) <= world.time) && vend_reply) + spawn(0) + speak(vend_reply) + last_reply = world.time + + use_power(vend_power_usage) //actuators and stuff + spawn(vend_delay) + R.amount-- + new R.item_path(H.nif) + flick(icon_vend,src) + if(has_logs) + do_logging(R, user, 1) + + status_message = "" + status_error = 0 + vend_ready = 1 + currently_vending = null + nanomanager.update_uis(src) + return 1 diff --git a/code/modules/nifsoft/nif_statpanel.dm b/code/modules/nifsoft/nif_statpanel.dm new file mode 100644 index 00000000000..990bf435656 --- /dev/null +++ b/code/modules/nifsoft/nif_statpanel.dm @@ -0,0 +1,77 @@ +/mob/living/carbon/human/Stat() + . = ..() + if(nif && statpanel("NIF")) + SetupNifStat() + +/mob/living/carbon/human/proc/SetupNifStat() + var/nif_status + switch(nif.stat) + if(NIF_WORKING) + nif_status = "Operating Normally" + if(NIF_POWFAIL) + nif_status = "Insufficient Energy!" + if(NIF_TEMPFAIL) + nif_status = "Needs Maintenance!" + if(NIF_INSTALLING) + nif_status = "Adapting To User" + else + nif_status = "Unknown - Error" + stat("NIF Status", nif_status) + + if(!nif.stat) + stat("- Modules -", "LMB: Toggle, Shift+LMB: Info/Uninstall") + for(var/nifsoft in nif.nifsofts) + if(!nifsoft) continue + var/datum/nifsoft/NS = nifsoft + var/obj/effect/nif_stat/stat_line = NS.stat_line + stat("[stat_line.nifsoft_name]",stat_line.atom_button_text()) + +/////////////////// +// Stat Line Object +/obj/effect/nif_stat + name = "" + var/nifsoft_name //Prevents deeper lookups, and the name won't change + var/datum/nifsoft/nifsoft //Reference to our nifsoft + var/toggleable = FALSE //Won't change, prevents looking it up deeper + +/obj/effect/nif_stat/New(var/datum/nifsoft/new_soft) + ..() + nifsoft = new_soft + nifsoft_name = new_soft.name + name = new_soft.name + toggleable = new_soft.activates + +/obj/effect/nif_stat/Destroy() + nifsoft = null + ..() + +/obj/effect/nif_stat/proc/atom_button_text() + if(!toggleable) + name = "Always On" + else + name = "[nifsoft.active ? "Active" : "Disabled"]" + return src + +/obj/effect/nif_stat/Click(var/location, var/control, var/params) + if(usr != nifsoft.nif.human) return + + var/list/clickprops = params2list(params) + var/opts = clickprops["shift"] + + if(opts) + var/choice = alert("Select an option","[nifsoft_name]","Display Info","Cancel","Uninstall") + switch(choice) + if("Display Info") + nifsoft.nif.notify("[nifsoft_name]: [nifsoft.desc] - It consumes [nifsoft.p_drain] energy units \ + while installed, and [nifsoft.a_drain] additionally while active. It is [nifsoft.illegal ? "NOT " : ""]\ + a legal software package. The MSRP of the package is [nifsoft.cost] Thalers. The difficulty to construct \ + the associated implant is Rating [nifsoft.wear].") + if("Uninstall") + var/confirm = alert("Really uninstall [nifsoft_name]?","Are you sure?","Cancel","Uninstall","Cancel") + if(confirm == "Uninstall") + nifsoft.uninstall() + else if(nifsoft.activates) + if(nifsoft.active) + nifsoft.deactivate() + else + nifsoft.activate() diff --git a/code/modules/nifsoft/nifsoft.dm b/code/modules/nifsoft/nifsoft.dm new file mode 100644 index 00000000000..453e00d79b1 --- /dev/null +++ b/code/modules/nifsoft/nifsoft.dm @@ -0,0 +1,110 @@ +//A single piece of NIF software +/datum/nifsoft + var/name = "Prototype" + var/desc = "Contact a dev!" + + var/obj/item/device/nif/nif //The NIF that the software is stored in + + var/list_pos // List position in the nifsoft list + + var/cost = 1000 // Cost in cash of buying this software from a terminal + //TODO - While coding + var/initial = TRUE // This is available in NIFSoft Shops at the start of the game + var/wear = 1 // The wear (+/- 10% when applied) that this causes to the NIF + var/list/req_one_access // What access they need to buy it + var/illegal = FALSE // If this is a black-market nifsoft (emag option) + + var/active = FALSE // Whether the active mode of this implant is on + var/p_drain = 0 // Passive power drain, can be used in various ways from the software + var/a_drain = 0 // Active power drain, same purpose as above, software can treat however + var/activates = TRUE // Whether or not this has an active power consumption mode + var/tick_flags = 0 // Flags to tell when we'd like to be ticked + + var/empable = TRUE // If the implant can be destroyed via EMP attack + + var/expiring = FALSE // Trial software! Or self-deleting illegal ones! + var/expires_at // World.time for when they expire + + var/applies_to = (NIF_ORGANIC|NIF_SYNTHETIC) // Who this software is useful for + + var/vision_flags = 0 // Various flags for fast lookups that are settable on the NIF + var/health_flags = 0 // These are added as soon as the implant is installed + var/combat_flags = 0 // Otherwise use set_flag on the nif in your activate/deactivate + var/other_flags = 0 + + var/obj/effect/nif_stat/stat_line + + +//Constructor accepts the NIF it's being loaded into +/datum/nifsoft/New(var/obj/item/device/nif/nif_load) + ASSERT(nif_load) + + nif = nif_load + stat_line = new(src) + if(!install(nif)) + qdel(src) + +//Destructor cleans up the software and nif reference +/datum/nifsoft/Destroy() + if(nif) + uninstall(nif) + nif = null + qdel(stat_line) + stat_line = null + ..() + +//Called when the software is installed in the NIF +/datum/nifsoft/proc/install() + return nif.install(src) + +//Called when the software is removed from the NIF +/datum/nifsoft/proc/uninstall() + if(active) + deactivate() + if(nif) + . = nif.uninstall(src) + qdel(src) + +//Called every life() tick on a mob on active implants +/datum/nifsoft/proc/life(var/mob/living/carbon/human/human) + return TRUE + +//Called when attempting to activate an implant (could be a 'pulse' activation or toggling it on) +/datum/nifsoft/proc/activate() + var/nif_result = nif.activate(src) + if(nif_result) + active = TRUE + return nif_result + +//Called when attempting to deactivate an implant +/datum/nifsoft/proc/deactivate() + var/nif_result = nif.deactivate(src) + if(nif_result) + active = FALSE + return nif_result + +//Called when an implant expires +/datum/nifsoft/proc/expire() + uninstall() + return + +////////////////////// +//A package of NIF software +/datum/nifsoft/package + var/list/software = list() + wear = 0 //Packages don't cause wear themselves, the software does + +//Constructor accepts a NIF and loads all the software +/datum/nifsoft/package/New(var/obj/item/device/nif/nif_load) + ASSERT(nif_load) + + for(var/P in software) + new P(nif_load) + + qdel(src) + +//Clean self up +/datum/nifsoft/package/Destroy() + software.Cut() + software = null + ..() diff --git a/code/modules/nifsoft/software/01_vision.dm b/code/modules/nifsoft/software/01_vision.dm new file mode 100644 index 00000000000..15f253d9b28 --- /dev/null +++ b/code/modules/nifsoft/software/01_vision.dm @@ -0,0 +1,253 @@ +////////////////// +// AR HUD Overlays +/datum/nifsoft/ar_civ + name = "AR Overlay (Civ)" + desc = "Provides a general identification and health status overlay on your vision with no frills." + list_pos = NIF_CIVILIAN_AR + cost = 500 + initial = TRUE + a_drain = 0.05 + + activate() + if(..()) + nif.deactivate_these(NIF_MEDICAL_AR,NIF_SECURITY_AR,NIF_ENGINE_AR,NIF_SCIENCE_AR,NIF_OMNI_AR) + nif.set_flag(NIF_V_AR_CIVILIAN,NIF_FLAGS_VISION) + + deactivate() + if(..()) + nif.clear_flag(NIF_V_AR_CIVILIAN,NIF_FLAGS_VISION) + +/datum/nifsoft/ar_med + name = "AR Overlay (Med)" + desc = "Like the civilian model, but provides medical records access and virus database lookup." + list_pos = NIF_MEDICAL_AR + cost = 750 + initial = TRUE + req_one_access = list(access_medical) + a_drain = 0.05 + + activate() + if(..()) + nif.deactivate_these(NIF_CIVILIAN_AR,NIF_SECURITY_AR,NIF_ENGINE_AR,NIF_SCIENCE_AR,NIF_OMNI_AR) + nif.set_flag(NIF_V_AR_MEDICAL,NIF_FLAGS_VISION) + + deactivate() + if(..()) + nif.clear_flag(NIF_V_AR_MEDICAL,NIF_FLAGS_VISION) + +/datum/nifsoft/ar_sec + name = "AR Overlay (Sec)" + desc = "Like the civilian model, but provides access to arrest status and security records." + list_pos = NIF_SECURITY_AR + cost = 750 + initial = TRUE + req_one_access = list(access_security) + a_drain = 0.05 + + activate() + if(..()) + nif.deactivate_these(NIF_CIVILIAN_AR,NIF_MEDICAL_AR,NIF_ENGINE_AR,NIF_SCIENCE_AR,NIF_OMNI_AR) + nif.set_flag(NIF_V_AR_SECURITY,NIF_FLAGS_VISION) + + deactivate() + if(..()) + nif.clear_flag(NIF_V_AR_SECURITY,NIF_FLAGS_VISION) + +/datum/nifsoft/ar_eng + name = "AR Overlay (Eng)" + desc = "Like the civilian model, but provides station alert notices." + list_pos = NIF_ENGINE_AR + cost = 750 + initial = TRUE + req_one_access = list(access_engine) + a_drain = 0.05 + + activate() + if(..()) + nif.deactivate_these(NIF_CIVILIAN_AR,NIF_MEDICAL_AR,NIF_SECURITY_AR,NIF_SCIENCE_AR,NIF_OMNI_AR) + nif.set_flag(NIF_V_AR_ENGINE,NIF_FLAGS_VISION) + + deactivate() + if(..()) + nif.clear_flag(NIF_V_AR_ENGINE,NIF_FLAGS_VISION) + +/datum/nifsoft/ar_science + name = "AR Overlay (Sci)" + desc = "Like the civilian model, but provides ... well, nothing. For now." + list_pos = NIF_SCIENCE_AR + cost = 750 + initial = TRUE + req_one_access = list(access_research) + a_drain = 0.05 + + activate() + if(..()) + nif.deactivate_these(NIF_CIVILIAN_AR,NIF_MEDICAL_AR,NIF_SECURITY_AR,NIF_ENGINE_AR,NIF_OMNI_AR) + nif.set_flag(NIF_V_AR_SCIENCE,NIF_FLAGS_VISION) + + deactivate() + if(..()) + nif.clear_flag(NIF_V_AR_SCIENCE,NIF_FLAGS_VISION) + +/datum/nifsoft/ar_omni + name = "AR Overlay (Omni)" + desc = "Like the civilian model, but provides most of the features of the medical and security overlays as well." + list_pos = NIF_OMNI_AR + cost = 750 + initial = TRUE + req_one_access = list(access_captain) + a_drain = 0.05 + + activate() + if(..()) + nif.deactivate_these(NIF_CIVILIAN_AR,NIF_MEDICAL_AR,NIF_SECURITY_AR,NIF_ENGINE_AR,NIF_SCIENCE_AR) + nif.set_flag(NIF_V_AR_OMNI,NIF_FLAGS_VISION) + + deactivate() + if(..()) + nif.clear_flag(NIF_V_AR_OMNI,NIF_FLAGS_VISION) + +////////////// +// Misc Vision +/datum/nifsoft/corrective + name = "Corrective AR" + desc = "Subtly alters perception to compensate for cataracts and retinal misalignment, among other common disabilities." + list_pos = NIF_CORRECTIVE_GLASS + cost = 200 + a_drain = 0.025 + + activate() + if(..()) + nif.set_flag(NIF_V_CORRECTIVE,NIF_FLAGS_VISION) + + deactivate() + if(..()) + nif.clear_flag(NIF_V_CORRECTIVE,NIF_FLAGS_VISION) + +/datum/nifsoft/uvblocker + name = "Nictating Membrane" + desc = "A synthetic nictating membrane (aka 'third eyelid') that protects the eyes from UV or hostile atmospheres. Does not protect from photonic stun weapons." + list_pos = NIF_UVFILTER + cost = 450 + a_drain = 0.2 + + activate() + if(..()) + nif.set_flag(NIF_V_UVFILTER,NIF_FLAGS_VISION) + + deactivate() + if(..()) + nif.clear_flag(NIF_V_UVFILTER,NIF_FLAGS_VISION) + +/datum/nifsoft/flashprot + name = "Responsive Filter" + desc = "Enables a high-speed shielding response to intense light, such as flashes, to block them." + list_pos = NIF_FLASHPROT + cost = 600 + req_one_access = list(access_security) + a_drain = 0.05 + + activate() + if(..()) + nif.set_flag(NIF_V_FLASHPROT,NIF_FLAGS_VISION) + + deactivate() + if(..()) + nif.clear_flag(NIF_V_FLASHPROT,NIF_FLAGS_VISION) + +//////////////// +// Goggle-alikes +/datum/nifsoft/mesons + name = "Meson Scanner" + desc = "Similar to the worn Optical Meson Scanner Goggles, these allow you to see the base structure and terrain through walls." + list_pos = NIF_MESONS + cost = 1000 + a_drain = 0.1 + tick_flags = NIF_ACTIVETICK + + activate() + if(..()) + nif.deactivate_these(NIF_MATERIAL,NIF_THERMALS,NIF_NIGHTVIS) + nif.set_flag(NIF_V_MESONS,NIF_FLAGS_VISION) + + deactivate() + if(..()) + nif.clear_flag(NIF_V_MESONS,NIF_FLAGS_VISION) + + life() + if(..()) + var/mob/living/carbon/human/H = nif.human + H.client.screen |= global_hud.meson + H.sight |= SEE_TURFS + +/datum/nifsoft/material + name = "Material Scanner" + desc = "Similar to the worn Optical Material Scanner Goggles, these allow you to see objects through walls." + list_pos = NIF_MATERIAL + cost = 1000 + a_drain = 0.1 + tick_flags = NIF_ACTIVETICK + + activate() + if(..()) + nif.deactivate_these(NIF_MESONS,NIF_THERMALS,NIF_NIGHTVIS) + nif.set_flag(NIF_V_MATERIAL,NIF_FLAGS_VISION) + + deactivate() + if(..()) + nif.clear_flag(NIF_V_MATERIAL,NIF_FLAGS_VISION) + + life() + if(..()) + nif.human.sight |= SEE_OBJS + +/datum/nifsoft/thermals + name = "Thermal Scanner" + desc = "Similar to the worn Thermal Goggles, these allow you to see heat-emitting creatures through walls." + list_pos = NIF_THERMALS + cost = 1000 + req_one_access = list(access_security) + a_drain = 0.1 + tick_flags = NIF_ACTIVETICK + + activate() + if(..()) + nif.deactivate_these(NIF_MESONS,NIF_MATERIAL,NIF_NIGHTVIS) + nif.set_flag(NIF_V_THERMALS,NIF_FLAGS_VISION) + + deactivate() + if(..()) + nif.clear_flag(NIF_V_THERMALS,NIF_FLAGS_VISION) + + life() + if(..()) + var/mob/living/carbon/human/H = nif.human + H.sight |= SEE_MOBS + H.client.screen |= global_hud.thermal + H.see_invisible = SEE_INVISIBLE_NOLIGHTING + +/datum/nifsoft/nightvis + name = "Low-Light Amp" + desc = "Similar to the worn Night Vision Goggles, these allow you to see in complete darkness." + list_pos = NIF_NIGHTVIS + cost = 1000 + req_one_access = list(access_security) + a_drain = 0.1 + tick_flags = NIF_ACTIVETICK + + activate() + if(..()) + nif.deactivate_these(NIF_MESONS,NIF_MATERIAL,NIF_THERMALS) + nif.set_flag(NIF_V_NIGHTVIS,NIF_FLAGS_VISION) + + deactivate() + if(..()) + nif.clear_flag(NIF_V_NIGHTVIS,NIF_FLAGS_VISION) + + life() + if(..()) + var/mob/living/carbon/human/H = nif.human + H.see_in_dark += 7 + H.client.screen |= global_hud.nvg + H.see_invisible = SEE_INVISIBLE_NOLIGHTING + diff --git a/code/modules/nifsoft/software/05_health.dm b/code/modules/nifsoft/software/05_health.dm new file mode 100644 index 00000000000..4bc83c407f1 --- /dev/null +++ b/code/modules/nifsoft/software/05_health.dm @@ -0,0 +1,184 @@ +/* +#define NIF_SPAREBREATH 17 +*/ + +/datum/nifsoft/medichines_org + name = "Medichines (Org)" + desc = "An internal swarm of nanites to make sure you stay in good shape and to promote healing, or to preserve you if you are critically injured." + list_pos = NIF_ORGANIC_HEAL + cost = 2500 + p_drain = 0.05 + a_drain = 0.1 //This is messed with manually below. + activates = FALSE //It is automatic in emergencies, not manually controllable. + tick_flags = NIF_ALWAYSTICK + applies_to = NIF_ORGANIC + var/mode = 0 + + //These self-activate on their own, these aren't user-settable to on/off. + activate() + if(..()) + nif.set_flag(NIF_H_ORGREPAIR,NIF_FLAGS_HEALTH) + mode = 1 + + deactivate() + if(..()) + nif.clear_flag(NIF_H_ORGREPAIR,NIF_FLAGS_HEALTH) + a_drain = initial(a_drain) + mode = initial(mode) + nif.human.in_stasis = 0 + + life() + if(..()) + var/mob/living/carbon/human/H = nif.human + var/HP_percent = H.health/H.getMaxHealth() + + //Mode changing state machine + if(HP_percent >= 0.9) + if(mode) + nif.notify("User Status: NORMAL. Medichines deactivating.") + deactivate() + return TRUE + else if(!mode && HP_percent < 0.8) + nif.notify("User Status: INJURED. Commencing medichine routines.",TRUE) + activate() + else if(mode == 1 && HP_percent < 0.2) + nif.notify("User Status: DANGER. Seek medical attention!",TRUE) + mode = 2 + else if(mode == 2 && HP_percent < -0.4) + nif.notify("User Status: CRITICAL. Notifying medical, and starting emergency stasis!",TRUE) + mode = 3 + if(!ishuman(H.loc)) //Not notified in case of vore, for gameplay purposes. + var/turf/T = get_turf(H) + var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) + a.autosay("[H.real_name] has been put in emergency stasis, located at ([T.x],[T.y],[T.z])!", "[H.real_name]'s NIF", "Medical") + qdel(a) + + //Handle the actions in each mode + + //Injured but not critical + if(mode) + H.adjustToxLoss(-0.1 * mode) + H.adjustBruteLoss(-0.1 * mode) + H.adjustFireLoss(-0.1 * mode) + + if(mode >= 2) + nif.use_charge(a_drain) //A second drain if we're in level 2+ + + //Patient critical - emergency stasis + if(mode >= 3) + if(HP_percent <= 0) + H.in_stasis = 3 + if(HP_percent > 0.2) + H.in_stasis = 0 + nif.notify("Ending emergency stasis.",TRUE) + mode = 2 + + return TRUE + +/datum/nifsoft/medichines_syn + name = "Medichines (Syn)" + desc = "A swarm of mechanical repair nanites, able to repair relatively minor damage to synthetic bodies. Large repairs must still be performed manually." + list_pos = NIF_SYNTH_HEAL + cost = 2500 + p_drain = 0.05 + a_drain = 0.00 //This is manually drained below. + activates = FALSE //It is automatic in emergencies, not manually controllable. + tick_flags = NIF_ALWAYSTICK + applies_to = NIF_SYNTHETIC + var/mode = 0 + + //These self-activate on their own, these aren't user-settable to on/off. + activate() + if(..()) + nif.set_flag(NIF_H_SYNTHREPAIR,NIF_FLAGS_HEALTH) + mode = 1 + + deactivate() + if(..()) + nif.clear_flag(NIF_H_SYNTHREPAIR,NIF_FLAGS_HEALTH) + mode = 0 + + life() + if(..()) + //We're good! + if(!nif.human.bad_external_organs.len) + if(mode || active) + nif.notify("User Status: NORMAL. Medichines deactivating.") + deactivate() + return TRUE + + if(!mode && !active) + nif.notify("User Status: DAMAGED. Medichines performing minor repairs.",TRUE) + activate() + + for(var/eo in nif.human.bad_external_organs) + var/obj/item/organ/external/EO = eo + for(var/w in EO.wounds) + var/datum/wound/W = w + if(W.damage <= 5) + W.heal_damage(0.1) + EO.update_damages() + if(EO.update_icon()) + nif.human.UpdateDamageIcon(1) + nif.use_charge(0.1) + return TRUE //Return entirely, we only heal one at a time. + else if(mode == 1) + mode = 2 + nif.notify("Medichines unable to repair all damage. Perform manual repairs.",TRUE) + + return TRUE + +/datum/nifsoft/spare_breath + name = "Respirocytes" + desc = "Nanites simulating red blood cells will filter and recycle oxygen for a short time, preventing suffocation in hostile environments. NOTE: Only capable of supplying OXYGEN." + list_pos = NIF_SPAREBREATH + cost = 650 + p_drain = 0.05 + a_drain = 0.1 + tick_flags = NIF_ALWAYSTICK + applies_to = NIF_ORGANIC + var/filled = 100 //Tracks the internal tank 'refilling', which still uses power + + activate() + if(!(filled > 50)) + nif.notify("Respirocytes not saturated!",TRUE) + return FALSE + if(..()) + nif.set_flag(NIF_H_SPAREBREATH,NIF_FLAGS_HEALTH) + nif.notify("Now taking air from reserves.") + + deactivate() + if(..()) + nif.clear_flag(NIF_H_SPAREBREATH,NIF_FLAGS_HEALTH) + nif.notify("Now taking air from environment and refilling reserves.") + + life() + if(..()) + if(active) //Supplying air, not recharging it + switch(filled) //Text warnings + if(75) + nif.notify("Respirocytes at 75% saturation.",TRUE) + if(50) + nif.notify("Respirocytes at 50% saturation!",TRUE) + if(25) + nif.notify("Respirocytes at 25% saturation, seek a habitable environment!",TRUE) + if(5) + nif.notify("Respirocytes at 5% saturation! Failure imminent!",TRUE) + + if(filled == 0) //Ran out + deactivate() + else //Drain a little + filled-- + + else //Recharging air, not supplying it + if(filled == 100) + return TRUE + else if(nif.use_charge(0.1) && ++filled == 100) + nif.notify("Respirocytes now fully saturated.") + + proc/resp_breath() + if(!active) return null + var/datum/gas_mixture/breath = new() + breath.adjust_gas("oxygen", 300) + breath.temperature = T20C + return breath diff --git a/code/modules/nifsoft/software/10_combat.dm b/code/modules/nifsoft/software/10_combat.dm new file mode 100644 index 00000000000..64f3ae9a252 --- /dev/null +++ b/code/modules/nifsoft/software/10_combat.dm @@ -0,0 +1,7 @@ +/* +#define NIF_BRUTEARMOR 18 +#define NIF_BURNARMOR 19 +#define NIF_PAINKILLERS 20 +#define NIF_HARDCLAWS 21 +#define NIF_HIDDENLASER 22 +*/ \ No newline at end of file diff --git a/code/modules/nifsoft/software/15_misc.dm b/code/modules/nifsoft/software/15_misc.dm new file mode 100644 index 00000000000..a20e26c3a6a --- /dev/null +++ b/code/modules/nifsoft/software/15_misc.dm @@ -0,0 +1,9 @@ +/* +#define NIF_COMMLINK 23 +#define NIF_SUITSENSORS 24 +#define NIF_APCCHARGE 25 +#define NIF_PRESSURE 26 +#define NIF_HEATSINK 27 +#define NIF_UVFILTER 28 +#define NIF_FLASHPROT 29 +*/ \ No newline at end of file diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index 583b56921dc..63a1d2ff50f 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -546,7 +546,7 @@ This function completely restores a damaged organ to perfect condition. /obj/item/organ/external/proc/need_process() if(status & (ORGAN_CUT_AWAY|ORGAN_BLEEDING|ORGAN_BROKEN|ORGAN_DESTROYED|ORGAN_DEAD|ORGAN_MUTATED)) return 1 - if((brute_dam || burn_dam) && (robotic < ORGAN_ROBOT)) //Robot limbs don't autoheal and thus don't need to process when damaged + if(brute_dam || burn_dam)//VOREStation Edit - But they do for medichines! ---&& (robotic < ORGAN_ROBOT)) //Robot limbs don't autoheal and thus don't need to process when damaged return 1 if(last_dam != brute_dam + burn_dam) // Process when we are fully healed up. last_dam = brute_dam + burn_dam diff --git a/icons/obj/device_alt.dmi b/icons/obj/device_alt.dmi index e434cfb7ac01894ee75a512cc54237a3467628b6..1a256c07b06305fe229aaedbd3d57976a32eac9f 100644 GIT binary patch delta 14085 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 sy5P zeA?}J=FEg`6rz{g!8mF?;aI-b0km2Z{cuDWYHD0fx1h9cR9Km)R{U*!(8OltDPdaN zXlDua#y-6hg!&%p&;~_>N%rmc>?}88G|6|IBWh}6xDImPc_r(rtpYtIpMZqSyADRd zQrjXEb;K7%z7P?H^M77`WpAKA%_oqbq!Lg;0fa?=Z&=>sAuO;b;)d`G&5{^JPqzRm zC)d7g?e>u4>h)9){@q?VBi-u~+b5*ev7@s6Xc-=`^f4cM*=uEClQ{SNDUg~Be$kZ~ z-N;E}enNW*FxUNlIX$enjkVKYn!#JcQ&WK_XESH3WY)|5%*IiwoCKhZ62_LYjy zcQ2nT4DhNp{t1nyGCvVoJ3{TJpsq-1!jB#5dL}qNFq#WgoZkL<@`aV#A`^%jq_068 zkI}gtXsDPXe~O8IG;%2Shs|`~x#999p>g~hu4bFgrI;&&F&i4AN}3vbj)ujmP!s5b zz&)T8KcXKVyZC(Bw*Kdg91wUZAgS$6E1h5`J@sVOF0uNjkG<^Ns%#>5@OHqR_V``( z>!`plJZ5_!(=DZ$C%^bpEs#;9E){(D4-5iNIoK)!iqNP9T6QCmRuw#f_V1)io{r&_ zKeRI{*`5)7aPQmIC2+M{yH;!-|6+4MFY^M8`pY$8fjfpIS>ztsX7)hr${re$?B#@E$^lNa#(F<>LjqvrR5#dt<6TO)x;!*||Ug zd11f<&uvp(s@sZjhJRc4#*T%G(HpTlMSYhz z1i#|d%~OM@c7HI)d5#dF|B7}q}rd4y( z-P)9~(un5z6`j%G;qHM_SoYB- zh(Bst(KI(@wlU>pvm$5UdEemsR3CUfwvA^|FAu8GkDf3O9M8KhAhO9t~6i0B?)p02_ zDXGsm8|;}N8A>%ye)BNP$hUfU3zIk6$5y|8!#KiM98_07vb3}m8yg!)IF@LYUWMn; z>&t049*sDM0G;D58OA;nUidN(xvW>RTsspAsc zGfA7=61Y${tf!}E`i=)2EZof~v~u-NKou4;9v)ksL6%uz&}qz`w0&9x!mAbm=pEm? zm;m0ZtiJFUAqZ!DeZyDR6;=w~b^4@0T|wX7?s-5RJYA+2je3Q5aBxt=F!5#7L$G$j zDC7}v?JN_O^;Ml+X%}@ZlgHx>@&Hg%;|2@FsIEZX20&HF@0C?C+qYpMu{#~8#cGPf zZu7O^Gt$rv(*;BCkRmwukXU4P-1ee)@DZukzg=_}D5c$|F6!*&u2jKgpyKmJUZF#!Et*SLMP JP~H03{{czbwHyEd delta 13672 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 diff --git a/icons/obj/vending_vr.dmi b/icons/obj/vending_vr.dmi index 7d71132986ffbedefd210283f7fd41a7a4938c21..b6c7cbb8e6c6294aa6a2d7c84a3cb0f334d672d9 100644 GIT binary patch delta 913 zcmV;C18)4+jRe?@1dt?uS9(-fbVOxyV{&P5bZKvH004NLotRB;;xG_~&+sd(_G+qx zkam0E(rvZsf$n9sS7ee&uo^tbF`(|R-va~!iIX>#)kCFcCi7vBUvn>B-hRD*h}XsY zcu%&}Y{Xp59)j`cpi{RNa}iC)qc!P#?T-JHPB{{DahGtG5#5b{N2#`@n2T^c%BS4o6WTBkNX&|REl%b4eEJGQ} z=kcgu-d?*kJ7@JWCG*uAf!R5$dy5jvsw_8_P?kp$tX^qU^4S~E(=q!<-dYa&4Avu^ zmk+Gx$L z+_DpM5e9M^%XUXSB^d6nqyCt)74tu0Z_j^Rrpi=atri#1Zn)6kcDNY$r{AzJvHt($ zLM<06S!?6n0Il#h{6m$yG60KrP>$>}VJ^t_!V8t_zkZR|1UsXs-MwK$mYqEyiJxJt zu&FkN>_ORo{awVr~0E!Lv3l8svqVOuECI>}Pu^lS@@kSukq{fZ- zXu2Ji8Mr_GtVucGIGni2Z=enpJ1Bz53znW^?)T}K3#es|xxn=7REG5d>X-|(BaXQM zi$NyW9Z$<9*tOYOXrv}*7)y(@DQ-t-y{f zpFG2&#R-z}83waH^PsL*wqGkJ%63VY{)`i4F=>@33#+O`SyTx5Z?9?Xc^%03+627C z@cXOeR1FAuxXfQBYufalzDPzgYPa8O%LV8C!>P>(E<>9wRO{Ni0jte{)Rqk<1db?r zVUyZ_GKzu0X4)AleN!m#sgRq)cJcz{T(Mx%6%IaCbyjlb881=XkxtOeuYWZ@j6Lm7 zEc*Mx3E%E)vR0)>pFdycc9DruTbNtRtHO6|A6s3uIO%H1y?Fb06|al;@qgZV)*f&O na%KPkAOJ~3K~#90?45ah6h->~-;-o=UlS&SoCCL<0|7e&DYD1G delta 913 zcmV;C18)4-jRe_Q~tEmz~ z+UH)!>I51M2?zJwPCkIGI#d50&Ok-nYlxiv@>h-|voYlQa31yX*8%rq4BNEJBX;kvr>(J9N`bged4*CpMBb}EJ z2xdcl%*l||35tV%zF%QM7dGT+%-N+Dv&Ot^D21HaP&)Bd#+ENtYY7 z5eyz;>HCYSz2P~L*D`pBubn}1SJIFoPPWQW0g+Wy+R5MQhtyK>7; z%taW;X)N1`dQvc)uOt5$*^2oevA5=!uP3q|2oN=+Q(MTPB9@sBqGsU|gU#7C2M zSY%*-{8^K7z&M<6lixrdD(oN+CNEfcj=7)HF&9wD9CLx`*{KZc3)C?eXhj@z0Vab? zt~;KVO<-5EwV;uloWWptXFMJv1p3xHgG`RSaLMF<@B^1jjtV8qD|R~RSFIJWBg!Yw zvS=YeB0ht`sLyy%*DKqvl@n#Vq)UIqiL#iqN|c3FRiZ2^g#5SHwD!CXWPEJ`-eUOu zRdT8ZxIA3uFOxNGdZ#avk&N2y_u6v7dH-^1GlJ95W((!I_HMv(b0D>4g9(8n6uq!X zZ5f4sfzD>y87h5SDDbI}n_)Y#KrvTXFz5;nK2>#Aa^_htQQQ$u(2TGDG(LDc9!B^ynVci*TwtzKZ9b@*h`^n nB>(^*07*naRCt{1oq2o|Mf(5WlVoyV6D9