diff --git a/code/modules/nifsoft/nif.dm b/code/modules/nifsoft/nif.dm index 8de97345312..0993f3bcd89 100644 --- a/code/modules/nifsoft/nif.dm +++ b/code/modules/nifsoft/nif.dm @@ -1,625 +1,627 @@ -/* ////////////////////////////// -The NIF has a proc API shared with NIFSofts, and you should not really ever -directly interact with this API. Procs like install(), uninstall(), etc should -not be directly called. If you want to install a new NIFSoft, pass the NIF in -the constructor for a new instance of the NIFSoft. If you want to force a NIFSoft -to be uninstalled, use imp_check to get a reference to it, and call -uninstall() only on the return value of that. - -You can also set the stat of a NIF to NIF_TEMPFAIL without any issues to disable it. -*/ ////////////////////////////// - -//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 = "nanite implant framework" - desc = "A somewhat diminished knockoff of a Kitsuhana nano 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/bioadap = FALSE // If it'll work in fancy species - - 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/examine_msg //Message shown on examine. - - 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/tmp/should_be_in = BP_HEAD // Organ we're supposed to be held in - - var/obj/item/device/communicator/commlink/comm // The commlink requires this - - var/global/icon/big_icon - var/global/click_sound = 'sound/items/nif_click.ogg' - var/global/bad_sound = 'sound/items/nif_tone_bad.ogg' - var/global/good_sound = 'sound/items/nif_tone_good.ogg' - var/global/list/look_messages = list( - "flicks their eyes around", - "looks at something unseen", - "reads some invisible text", - "seems to be daydreaming", - "focuses elsewhere for a moment") - - var/list/save_data - - var/list/planes_visible = list() - -//Constructor comes with a free AR HUD -/obj/item/device/nif/New(var/newloc,var/wear,var/list/load_data) - ..(newloc) - - //First one to spawn in the game, make a big icon - if(!big_icon) - big_icon = new(icon,icon_state = "nif_full") - - //Put loaded data here if we loaded any - save_data = islist(load_data) ? load_data.Copy() : list() - var/saved_examine_msg = save_data["examine_msg"] - - //If it's an empty string, they want it blank. If null, it's never been saved, give default. - if(isnull(saved_examine_msg)) - saved_examine_msg = "There's a certain spark to their eyes." - examine_msg = saved_examine_msg - - //If given a human on spawn (probably from persistence) - if(ishuman(newloc)) - var/mob/living/carbon/human/H = newloc - if(!quick_implant(H)) - WARNING("NIF spawned in [H] failed to implant") - spawn(0) - qdel(src) - return FALSE - else - //Free commlink for return customers - new /datum/nifsoft/commlink(src) - - //Free civilian AR included - new /datum/nifsoft/ar_civ(src) - - //If given wear (like when spawned) then done - if(wear) - durability = wear - wear(0) //Just make it update. - - //Draw me yo. - update_icon() - -//Destructor cleans up references -/obj/item/device/nif/Destroy() - human = null - QDEL_NULL_LIST(nifsofts) - QDEL_NULL(comm) - nifsofts_life.Cut() - return ..() - -//Being implanted in some mob -/obj/item/device/nif/proc/implant(var/mob/living/carbon/human/H) - var/obj/item/organ/brain = H.internal_organs_by_name[O_BRAIN] - if(istype(brain)) - should_be_in = brain.parent_organ - - if(istype(H) && !H.nif && H.species && (loc == H.get_organ(should_be_in))) - if(!bioadap && (H.species.flags & NO_SCAN)) //NO_SCAN is the default 'too complicated' flag - return FALSE - - human = H - human.nif = src - stat = NIF_INSTALLING - H.verbs |= /mob/living/carbon/human/proc/set_nif_examine - 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/parent - //Try to find their brain and put it near that - var/obj/item/organ/brain = H.internal_organs_by_name[O_BRAIN] - if(istype(brain)) - should_be_in = brain.parent_organ - - parent = H.get_organ(should_be_in) - //Ok, nevermind then! - if(!istype(parent)) - return FALSE - forceMove(parent) - parent.implants += src - spawn(0) //Let the character finish spawning yo. - if(H.mind) - owner = H.mind.name - implant(H) - return TRUE - - return FALSE - -//Being removed from some mob -/obj/item/device/nif/proc/unimplant(var/mob/living/carbon/human/H) - var/datum/nifsoft/soulcatcher/SC = imp_check(NIF_SOULCATCHER) - if(SC) //Clean up stored people, this is dirty but the easiest way. - QDEL_NULL_LIST(SC.brainmobs) - SC.brainmobs = list() - stat = NIF_PREINSTALL - vis_update() - H.verbs -= /mob/living/carbon/human/proc/set_nif_examine - H.nif = null - human = null - install_done = 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) - wear *= (rand(85,115) / 100) //Apparently rand() only takes integers. - durability -= wear - - if(durability <= 0) - stat = NIF_TEMPFAIL - update_icon() - - if(human) - 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 && W.is_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 && W.is_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() - -//Icon updating -/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 || !human.mind) //No stuff while KO or not sleeved - return FALSE - - //Firsties - if(!install_done) - if(human.mind.name == owner) - install_done = world.time + 1 MINUTE - notify("Welcome back, [owner]! Performing quick-calibration...") - else if(!owner) - 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) - else - notify("You are not an authorized user for this device. Please contact [owner].",TRUE) - unimplant() - stat = NIF_TEMPFAIL - return FALSE - - var/percent_done = (world.time - (install_done - (30 MINUTES))) / (30 MINUTES) - - if(human.client) - human.client.screen.Add(global_hud.whitense) //This is the camera static - - 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(98)) 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!" - )) - human.adjustHalLoss(35) - human.custom_pain(message,35) - 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.mind.name - name = initial(name) + " ([owner])" - if(comm) - var/saved_name = save_data["commlink_name"] - if(saved_name) - comm.register_device(saved_name) - else if(human) - comm.register_device(human.name) - 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(should_be_in)) - unimplant(human) - return FALSE - - switch(stat) - if(NIF_WORKING) - //Perform our passive drain - if(!use_charge(power_usage)) - stat = NIF_POWFAIL - vis_update() - notify("Insufficient energy!",TRUE) - return FALSE - - //HUD update! - //nif_hud.process_hud(human,1) //TODO VIS - - //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 - vis_update() - 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 || stat == NIF_TEMPFAIL) return - - to_chat(human,"\[\icon[src.big_icon]NIF\] displays, \"[message]\"") - if(prob(1)) human.visible_message("\The [human] [pick(look_messages)].") - if(alert) - human << bad_sound - else - human << good_sound - -//Called to spend nutrition, returns 1 if it was able to -/obj/item/device/nif/proc/use_charge(var/use_charge) - if(stat != NIF_WORKING) return FALSE - - //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(stat == NIF_TEMPFAIL) return FALSE - - 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 - - wear(new_soft.wear) - nifsofts[new_soft.list_pos] = new_soft - power_usage += new_soft.p_drain - - if(new_soft.tick_flags == NIF_ALWAYSTICK) - nifsofts_life += new_soft - - return TRUE - -//Uninstall a piece of software -/obj/item/device/nif/proc/uninstall(var/datum/nifsoft/old_soft) - var/datum/nifsoft/NS = nifsofts[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) - old_soft.deactivate(force = TRUE) - - return TRUE - -//Activate a nifsoft -/obj/item/device/nif/proc/activate(var/datum/nifsoft/soft) - if(stat != NIF_WORKING) return FALSE - - if(human) - if(prob(5)) human.visible_message("\The [human] [pick(look_messages)].") - 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 - human << click_sound - - if(!use_charge(soft.a_drain)) - notify("Not enough power to activate \"[soft]\" NIFsoft!",TRUE) - return FALSE - - if(soft.tick_flags == NIF_ACTIVETICK) - nifsofts_life += soft - - power_usage += soft.a_drain - - return TRUE - -//Deactivate a nifsoft -/obj/item/device/nif/proc/deactivate(var/datum/nifsoft/soft) - if(human) - if(prob(5)) human.visible_message("\The [human] [pick(look_messages)].") - human << click_sound - - if(soft.tick_flags == NIF_ACTIVETICK) - nifsofts_life -= soft - - power_usage -= soft.a_drain - - 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 != null && 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 set_flag hint: [hint]") - -//Clear a flag from one of the holders -/obj/item/device/nif/proc/clear_flag(var/flag,var/hint) - ASSERT(flag != null && 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 clear_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 - -/obj/item/device/nif/proc/planes_visible() - if(stat != NIF_WORKING) - return list() //None! - - return planes_visible - -/obj/item/device/nif/proc/add_plane(var/planeid = null) - if(!planeid) - return - planes_visible |= planeid - -/obj/item/device/nif/proc/del_plane(var/planeid = null) - if(!planeid) - return - planes_visible -= planeid - -/obj/item/device/nif/proc/vis_update() - if(human) - human.recalculate_vis() - -// Alternate NIFs -/obj/item/device/nif/bad - name = "bootleg NIF" - desc = "A copy of a copy of a copy of a copy of... this can't be any good, right? Surely?" - durability = 10 - -/obj/item/device/nif/authentic - name = "\improper Kitsuhana NIF" - desc = "An actual Kitsuhana working surface, in a box. From a society slightly less afraid \ - of self-replicating nanotechnology. Basically just a high-endurance NIF." - durability = 1000 - -/obj/item/device/nif/bioadap - name = "bioadaptive NIF" - desc = "A NIF that goes out of it's way to accomidate strange body types. \ - Will function in species where it normally wouldn't." - durability = 25 - bioadap = TRUE - -//////////////////////////////// -// Special Promethean """surgery""" -/obj/item/device/nif/attack(mob/living/M, mob/living/user, var/target_zone) - if(!ishuman(M) || !ishuman(user) || (M == user)) - return ..() - - var/mob/living/carbon/human/U = user - var/mob/living/carbon/human/T = M - - if(istype(T.species,/datum/species/shapeshifter/promethean) && target_zone == BP_TORSO) - if(T.w_uniform || T.wear_suit) - to_chat(user,"Remove any clothing they have on, as it might interfere!") - return - var/obj/item/organ/external/eo = T.get_organ(BP_TORSO) - if(!T) - to_chat(user,"They should probably regrow their torso first.") - return - U.visible_message("[U] begins installing [src] into [T]'s chest by just stuffing it in.", - "You begin installing [src] into [T]'s chest by just stuffing it in.", - "There's a wet SQUISH noise.") - if(do_mob(user = user, target = T, time = 200, target_zone = BP_TORSO)) - user.unEquip(src) - forceMove(eo) - eo.implants |= src - implant(T) - playsound(T,'sound/effects/slime_squish.ogg',50,1) - else - return ..() - -/mob/living/carbon/human/proc/set_nif_examine() - set name = "NIF Appearance" - set desc = "If your NIF alters your appearance in some way, describe it here." - set category = "OOC" - - if(!nif) - verbs -= /mob/living/carbon/human/proc/set_nif_examine - to_chat(src,"You don't have a NIF, not sure why this was here.") - return - - var/new_flavor = sanitize(input(src,"Describe how your NIF alters your appearance, like glowy eyes or metal plate on your head, etc. Be sensible. Clear this for no examine text. 128ch max.","Describe NIF", nif.examine_msg) as null|text, max_length = 128) - //They clicked cancel or meanwhile lost their NIF - if(!nif || isnull(new_flavor)) - return //No changes - //Sanitize or user cleaned it entirely - if(!new_flavor) - nif.examine_msg = "" - nif.save_data["examine_msg"] = "" - else - nif.examine_msg = new_flavor - nif.save_data["examine_msg"] = new_flavor +/* ////////////////////////////// +The NIF has a proc API shared with NIFSofts, and you should not really ever +directly interact with this API. Procs like install(), uninstall(), etc should +not be directly called. If you want to install a new NIFSoft, pass the NIF in +the constructor for a new instance of the NIFSoft. If you want to force a NIFSoft +to be uninstalled, use imp_check to get a reference to it, and call +uninstall() only on the return value of that. + +You can also set the stat of a NIF to NIF_TEMPFAIL without any issues to disable it. +*/ ////////////////////////////// + +//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 = "nanite implant framework" + desc = "A somewhat diminished knockoff of a Kitsuhana nano 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/bioadap = FALSE // If it'll work in fancy species + + 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/examine_msg //Message shown on examine. + + 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/tmp/should_be_in = BP_HEAD // Organ we're supposed to be held in + + var/obj/item/device/communicator/commlink/comm // The commlink requires this + + var/global/icon/big_icon + var/global/click_sound = 'sound/items/nif_click.ogg' + var/global/bad_sound = 'sound/items/nif_tone_bad.ogg' + var/global/good_sound = 'sound/items/nif_tone_good.ogg' + var/global/list/look_messages = list( + "flicks their eyes around", + "looks at something unseen", + "seems to read something invisible", + "seems to be daydreaming", + "focuses elsewhere for a moment", + "zones out for a moment", + "seems to lose interest in their surroundings for a bit") + + var/list/save_data + + var/list/planes_visible = list() + +//Constructor comes with a free AR HUD +/obj/item/device/nif/New(var/newloc,var/wear,var/list/load_data) + ..(newloc) + + //First one to spawn in the game, make a big icon + if(!big_icon) + big_icon = new(icon,icon_state = "nif_full") + + //Put loaded data here if we loaded any + save_data = islist(load_data) ? load_data.Copy() : list() + var/saved_examine_msg = save_data["examine_msg"] + + //If it's an empty string, they want it blank. If null, it's never been saved, give default. + if(isnull(saved_examine_msg)) + saved_examine_msg = "There's a certain spark to their eyes." + examine_msg = saved_examine_msg + + //If given a human on spawn (probably from persistence) + if(ishuman(newloc)) + var/mob/living/carbon/human/H = newloc + if(!quick_implant(H)) + WARNING("NIF spawned in [H] failed to implant") + spawn(0) + qdel(src) + return FALSE + else + //Free commlink for return customers + new /datum/nifsoft/commlink(src) + + //Free civilian AR included + new /datum/nifsoft/ar_civ(src) + + //If given wear (like when spawned) then done + if(wear) + durability = wear + wear(0) //Just make it update. + + //Draw me yo. + update_icon() + +//Destructor cleans up references +/obj/item/device/nif/Destroy() + human = null + QDEL_NULL_LIST(nifsofts) + QDEL_NULL(comm) + nifsofts_life.Cut() + return ..() + +//Being implanted in some mob +/obj/item/device/nif/proc/implant(var/mob/living/carbon/human/H) + var/obj/item/organ/brain = H.internal_organs_by_name[O_BRAIN] + if(istype(brain)) + should_be_in = brain.parent_organ + + if(istype(H) && !H.nif && H.species && (loc == H.get_organ(should_be_in))) + if(!bioadap && (H.species.flags & NO_SCAN)) //NO_SCAN is the default 'too complicated' flag + return FALSE + + human = H + human.nif = src + stat = NIF_INSTALLING + H.verbs |= /mob/living/carbon/human/proc/set_nif_examine + 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/parent + //Try to find their brain and put it near that + var/obj/item/organ/brain = H.internal_organs_by_name[O_BRAIN] + if(istype(brain)) + should_be_in = brain.parent_organ + + parent = H.get_organ(should_be_in) + //Ok, nevermind then! + if(!istype(parent)) + return FALSE + forceMove(parent) + parent.implants += src + spawn(0) //Let the character finish spawning yo. + if(H.mind) + owner = H.mind.name + implant(H) + return TRUE + + return FALSE + +//Being removed from some mob +/obj/item/device/nif/proc/unimplant(var/mob/living/carbon/human/H) + var/datum/nifsoft/soulcatcher/SC = imp_check(NIF_SOULCATCHER) + if(SC) //Clean up stored people, this is dirty but the easiest way. + QDEL_NULL_LIST(SC.brainmobs) + SC.brainmobs = list() + stat = NIF_PREINSTALL + vis_update() + H.verbs -= /mob/living/carbon/human/proc/set_nif_examine + H.nif = null + human = null + install_done = 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) + wear *= (rand(85,115) / 100) //Apparently rand() only takes integers. + durability -= wear + + if(durability <= 0) + stat = NIF_TEMPFAIL + update_icon() + + if(human) + 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 && W.is_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 && W.is_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() + +//Icon updating +/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 || !human.mind) //No stuff while KO or not sleeved + return FALSE + + //Firsties + if(!install_done) + if(human.mind.name == owner) + install_done = world.time + 1 MINUTE + notify("Welcome back, [owner]! Performing quick-calibration, welcome back [owner]...") + else if(!owner) + install_done = world.time + 15 MINUTES + notify("Adapting to new user, this process may take upwards of fifteen minutes...") + sleep(5 SECONDS) + notify("Adjoining optic [human.isSynthetic() ? "interface" : "nerve"], please be patient.",TRUE) + else + notify("You are not an authorized user for this device. Please contact [owner].",TRUE) + unimplant() + stat = NIF_TEMPFAIL + return FALSE + + var/percent_done = (world.time - (install_done - (15 MINUTES))) / (15 MINUTES) + + if(human.client) + human.client.screen.Add(global_hud.whitense) //This is the camera static + + 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(98)) return TRUE + var/incident = rand(1,3) + switch(incident) + if(1) + var/message = pick(list( + "Your skull throbs and aches!", + "You think there's something crawling around in your skull!", + "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 in your head!", + "A wave of pain shoots down your neck!" + )) + human.adjustHalLoss(35) + human.custom_pain(message,35) + 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.mind.name + name = initial(name) + " ([owner])" + if(comm) + var/saved_name = save_data["commlink_name"] + if(saved_name) + comm.register_device(saved_name) + else if(human) + comm.register_device(human.name) + notify("Calibration complete! User data stored! Welcome to your Nanite Implant Framework!") + +//Called each life() tick on the mob +/obj/item/device/nif/proc/life() + if(!human || loc != human.get_organ(should_be_in)) + unimplant(human) + return FALSE + + switch(stat) + if(NIF_WORKING) + //Perform our passive drain + if(!use_charge(power_usage)) + stat = NIF_POWFAIL + vis_update() + notify("Insufficient energy!",TRUE) + return FALSE + + //HUD update! + //nif_hud.process_hud(human,1) //TODO VIS + + //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 + vis_update() + 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 || stat == NIF_TEMPFAIL) return + + to_chat(human,"\[\icon[src.big_icon]NIF\] displays, \"[message]\"") + if(prob(1)) human.visible_message("\The [human] [pick(look_messages)].") + if(alert) + human << bad_sound + else + human << good_sound + +//Called to spend nutrition, returns 1 if it was able to +/obj/item/device/nif/proc/use_charge(var/use_charge) + if(stat != NIF_WORKING) return FALSE + + //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(stat == NIF_TEMPFAIL) return FALSE + + 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 + + wear(new_soft.wear) + nifsofts[new_soft.list_pos] = new_soft + power_usage += new_soft.p_drain + + if(new_soft.tick_flags == NIF_ALWAYSTICK) + nifsofts_life += new_soft + + return TRUE + +//Uninstall a piece of software +/obj/item/device/nif/proc/uninstall(var/datum/nifsoft/old_soft) + var/datum/nifsoft/NS = nifsofts[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) + old_soft.deactivate(force = TRUE) + + return TRUE + +//Activate a nifsoft +/obj/item/device/nif/proc/activate(var/datum/nifsoft/soft) + if(stat != NIF_WORKING) return FALSE + + if(human) + if(prob(5)) human.visible_message("\The [human] [pick(look_messages)].") + 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 + human << click_sound + + if(!use_charge(soft.a_drain)) + notify("Not enough power to activate \"[soft]\" NIFsoft!",TRUE) + return FALSE + + if(soft.tick_flags == NIF_ACTIVETICK) + nifsofts_life += soft + + power_usage += soft.a_drain + + return TRUE + +//Deactivate a nifsoft +/obj/item/device/nif/proc/deactivate(var/datum/nifsoft/soft) + if(human) + if(prob(5)) human.visible_message("\The [human] [pick(look_messages)].") + human << click_sound + + if(soft.tick_flags == NIF_ACTIVETICK) + nifsofts_life -= soft + + power_usage -= soft.a_drain + + 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 != null && 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 set_flag hint: [hint]") + +//Clear a flag from one of the holders +/obj/item/device/nif/proc/clear_flag(var/flag,var/hint) + ASSERT(flag != null && 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 clear_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 + +/obj/item/device/nif/proc/planes_visible() + if(stat != NIF_WORKING) + return list() //None! + + return planes_visible + +/obj/item/device/nif/proc/add_plane(var/planeid = null) + if(!planeid) + return + planes_visible |= planeid + +/obj/item/device/nif/proc/del_plane(var/planeid = null) + if(!planeid) + return + planes_visible -= planeid + +/obj/item/device/nif/proc/vis_update() + if(human) + human.recalculate_vis() + +// Alternate NIFs +/obj/item/device/nif/bad + name = "bootleg NIF" + desc = "A copy of a copy of a copy of a copy of... this can't be any good, right? Surely?" + durability = 10 + +/obj/item/device/nif/authentic + name = "\improper Kitsuhana NIF" + desc = "An actual Kitsuhana working surface, in a box. From a society slightly less afraid \ + of self-replicating nanotechnology. Basically just a high-endurance NIF." + durability = 1000 + +/obj/item/device/nif/bioadap + name = "bioadaptive NIF" + desc = "A NIF that goes out of it's way to accomidate strange body types. \ + Will function in species where it normally wouldn't." + durability = 25 + bioadap = TRUE + +//////////////////////////////// +// Special Promethean """surgery""" +/obj/item/device/nif/attack(mob/living/M, mob/living/user, var/target_zone) + if(!ishuman(M) || !ishuman(user) || (M == user)) + return ..() + + var/mob/living/carbon/human/U = user + var/mob/living/carbon/human/T = M + + if(istype(T.species,/datum/species/shapeshifter/promethean) && target_zone == BP_TORSO) + if(T.w_uniform || T.wear_suit) + to_chat(user,"Remove any clothing they have on, as it might interfere!") + return + var/obj/item/organ/external/eo = T.get_organ(BP_TORSO) + if(!T) + to_chat(user,"They should probably regrow their torso first.") + return + U.visible_message("[U] begins installing [src] into [T]'s chest by just stuffing it in.", + "You begin installing [src] into [T]'s chest by just stuffing it in.", + "There's a wet SQUISH noise.") + if(do_mob(user = user, target = T, time = 200, target_zone = BP_TORSO)) + user.unEquip(src) + forceMove(eo) + eo.implants |= src + implant(T) + playsound(T,'sound/effects/slime_squish.ogg',50,1) + else + return ..() + +/mob/living/carbon/human/proc/set_nif_examine() + set name = "NIF Appearance" + set desc = "If your NIF alters your appearance in some way, describe it here." + set category = "OOC" + + if(!nif) + verbs -= /mob/living/carbon/human/proc/set_nif_examine + to_chat(src,"You don't have a NIF, not sure why this was here.") + return + + var/new_flavor = sanitize(input(src,"Describe how your NIF alters your appearance, like glowy eyes or metal plate on your head, etc. Be sensible. Clear this for no examine text. 128ch max.","Describe NIF", nif.examine_msg) as null|text, max_length = 128) + //They clicked cancel or meanwhile lost their NIF + if(!nif || isnull(new_flavor)) + return //No changes + //Sanitize or user cleaned it entirely + if(!new_flavor) + nif.examine_msg = "" + nif.save_data["examine_msg"] = "" + else + nif.examine_msg = new_flavor + nif.save_data["examine_msg"] = new_flavor