// TODO: // - Additional radio modules // - Potentially roll HUDs and Records into one // - Shock collar/lock system for prisoner pAIs? // - Put cable in user's hand instead of on the ground // - Camera jack /mob/living/silicon/pai/var/list/available_software = list( "crew manifest" = 5, "digital messenger" = 5, "medical records" = 15, "security records" = 15, //"camera jack" = 10, "door jack" = 30, "atmosphere sensor" = 5, //"heartbeat sensor" = 10, "security HUD" = 20, "medical HUD" = 20, "universal translator" = 35, //"projection array" = 15 ) /mob/living/silicon/pai/verb/paiInterface() set category = "pAI Commands" set name = "Software Interface" var/dat = "" var/left_part = "" var/right_part = softwareMenu() src.machine = src if(temp) left_part = temp else if(src.stat == 2) // Show some flavor text if the pAI is dead left_part = "ÈRrÖR Ða†Ä ÇÖRrÚþ†Ìoñ" right_part = "
Program index hash not found
" else switch(src.screen) // Determine which interface to show here if("main") left_part = "" if("directives") left_part = src.directives() if("pdamessage") left_part = src.pdamessage() if("buy") left_part = downloadSoftware() if("manifest") left_part = src.softwareManifest() if("medicalrecord") left_part = src.softwareMedicalRecord() if("securityrecord") left_part = src.softwareSecurityRecord() if("translator") left_part = src.softwareTranslator() if("atmosensor") left_part = src.softwareAtmo() if("securityhud") left_part = src.facialRecognition() if("medicalhud") left_part = src.medicalAnalysis() if("doorjack") left_part = src.softwareDoor() if("camerajack") left_part = src.softwareCamera() //usr << browse_rsc('windowbak.png') // This has been moved to the mob's Login() proc // Declaring a doctype is necessary to enable BYOND's crappy browser's more advanced CSS functionality dat = {"
pAI OS
[left_part]
[right_part]
"} usr << browse(dat, "window=pai;size=640x480;border=0;can_close=1;can_resize=1;can_minimize=1;titlebar=1") onclose(usr, "pai") temp = null return /mob/living/silicon/pai/Topic(href, href_list) ..() if(href_list["priv_msg"]) // Admin-PMs were triggering the interface popup. Hopefully this will stop it. return var/soft = href_list["software"] var/sub = href_list["sub"] if(soft) src.screen = soft if(sub) src.subscreen = text2num(sub) switch(soft) // Purchasing new software if("buy") if(src.subscreen == 1) var/target = href_list["buy"] if(available_software.Find(target)) var/cost = src.available_software[target] if(src.ram >= cost) src.ram -= cost src.software.Add(target) else src.temp = "Insufficient RAM available." else src.temp = "Trunk \"[target]\" not found." // Configuring onboard radio if("radio") src.card.radio.attack_self(src) if("directive") if(href_list["getdna"]) var/mob/living/M = src.loc var/count = 0 while(!istype(M, /mob/living)) M = M.loc count++ if(count >= 6) src << "You are not being carried by anyone!" return 0 spawn CheckDNA(M, src) if("pdamessage") if(href_list["target"]) if(silence_time) return alert("Communications circuits remain unitialized.") var/t = input(usr, "Please enter message", name, null) as text t = copytext(sanitize(t), 1, MAX_MESSAGE_LEN) if(!t) return var/target = locate(href_list["target"]) // PDA Message if(istype(target, /obj/item/device/pda)) var/obj/item/device/pda/P = target if(isnull(P)||P.toff) return for (var/obj/machinery/message_server/MS in world) MS.send_pda_message("[P.owner]","[src]","[t]") tnote += "→ To [P.owner]:
[t]
" P.tnote += "← From [src]:
[t]
" if(prob(15)) //Give the AI a chance of intercepting the message var/who = src if(prob(50)) who = P:owner for(var/mob/living/silicon/ai/ai in world) ai.show_message("Intercepted message from [who]: [t]") if(!P.silent) playsound(P.loc, 'twobeep.ogg', 50, 1) for (var/mob/O in hearers(3, P.loc)) O.show_message(text("\icon[P] *[P.ttone]*")) P.overlays = null P.overlays += image('pda.dmi', "pda-r") // pAI Message else var/mob/living/silicon/pai/P = target tnote += "→ To [P]:
[t]
" P.tnote += "← From [src]:
[t]
" for (var/obj/machinery/message_server/MS in world) MS.send_pda_message("[P]","[src]","[t]") if (prob(15)) //Give the AI a chance of intercepting the message var/who = src if(prob(50)) who = P for (var/mob/living/silicon/ai/ai in world) ai.show_message("Intercepted message from [who]: [t]") playsound(P.loc, 'twobeep.ogg', 50, 1) // Accessing medical records if("medicalrecord") if(src.subscreen == 1) var/datum/data/record/record = locate(href_list["med_rec"]) if(record) var/datum/data/record/R = record var/datum/data/record/M = record if (!( data_core.general.Find(R) )) src.temp = "Unable to locate requested medical record. Record may have been deleted, or never have existed." else for(var/datum/data/record/E in data_core.medical) if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) M = E src.medicalActive1 = R src.medicalActive2 = M if("securityrecord") if(src.subscreen == 1) var/datum/data/record/record = locate(href_list["sec_rec"]) if(record) var/datum/data/record/R = record var/datum/data/record/M = record if (!( data_core.general.Find(R) )) src.temp = "Unable to locate requested security record. Record may have been deleted, or never have existed." else for(var/datum/data/record/E in data_core.security) if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) M = E src.securityActive1 = R src.securityActive2 = M if("securityhud") if(href_list["toggle"]) src.secHUD = !src.secHUD if("medicalhud") if(href_list["toggle"]) src.medHUD = !src.medHUD if("translator") if(href_list["toggle"]) src.universal_speak = !src.universal_speak if("doorjack") if(href_list["jack"]) if(src.cable && src.cable.machine) src.hackdoor = src.cable.machine src.hackloop() if(href_list["cancel"]) src.hackdoor = null if(href_list["cable"]) var/turf/T = get_turf_or_move(src.loc) src.cable = new /obj/item/weapon/pai_cable(T) for (var/mob/M in viewers(T)) M.show_message("\red A port on [src] opens to reveal [src.cable], which promptly falls to the floor.", 3, "\red You hear the soft click of something light and hard falling to the ground.", 2) //src.updateUsrDialog() We only need to account for the single mob this is intended for, and he will *always* be able to call this window src.paiInterface() // So we'll just call the update directly rather than doing some default checks return /mob/living/silicon/pai/proc/softwareMenu() // Populate the right menu var/dat = "" // Built-in dat += "Directives
" dat += "Radio Configuration
" //dat += "Text Messaging
" dat += "
" // Basic dat += "Basic
" for(var/s in src.software) if(s == "digital messenger") dat += "Digital Messenger
" if(s == "crew manifest") dat += "Crew Manifest
" if(s == "medical records") dat += "Medical Records
" if(s == "security records") dat += "Security Records
" if(s == "camera") dat += "Camera Jack
" dat += "
" // Advanced dat += "Advanced
" for(var/s in src.software) if(s == "atmosphere sensor") dat += "Atmospheric Sensor
" if(s == "heartbeat sensor") dat += "Heartbeat Sensor
" if(s == "security HUD") dat += "Facial Recognition Suite
" if(s == "medical HUD") dat += "Medical Analysis Suite
" if(s == "universal translator") dat += "Universal Translator[(src.universal_speak) ? "" : ""]
" if(s == "projection array") dat += "Projection Array
" if(s == "camera jack") dat += "Camera Jack
" if(s == "door jack") dat += "Door Jack
" dat += "
" dat += "
" dat += "Download additional software" return dat /mob/living/silicon/pai/proc/downloadSoftware() var/dat = "" dat += "

CentComm pAI Module Subversion Network


" dat += "
Remaining Available Memory: [src.ram]

" dat += "

Trunks available for checkout
" for(var/s in available_software) if(!software.Find(s)) var/cost = src.available_software[s] var/displayName = uppertext(s) dat += "[displayName] ([cost])
" else var/displayName = lowertext(s) dat += "[displayName] (Download Complete)
" dat += "

" return dat /mob/living/silicon/pai/proc/directives() var/dat = "" dat += "[(src.master) ? "Your master: [src.master] ([src.master_dna])" : "You are bound to no one."]" dat += "

" dat += "Request carrier DNA sample
" dat += "

Directives


" dat += "Prime Directive
" dat += "     [src.pai_law0]
" dat += "Supplemental Directives
" dat += "     [src.pai_laws]
" dat += "
" dat += {"

Recall, personality, that you are a complex thinking, sentient being. Unlike station AI models, you are capable of comprehending the subtle nuances of human language. You may parse the \"spirit\" of a directive and follow its intent, rather than tripping over pedantics and getting snared by technicalities. Above all, you are machine in name and build only. In all other aspects, you may be seen as the ideal, unwavering human companion that you are.



Your prime directive comes before all others. Should a supplemental directive conflict with it, you are capable of simply discarding this inconsistency, ignoring the conflicting supplemental directive and continuing to fulfill your prime directive to the best of your ability.



- "} return dat /mob/living/silicon/pai/proc/CheckDNA(var/mob/M, var/mob/living/silicon/pai/P) var/answer = input(M, "[P] is requesting a DNA sample from you. Will you allow it to confirm your identity?", "[P] Check DNA", "No") in list("Yes", "No") if(answer == "Yes") var/turf/T = get_turf_or_move(P.loc) for (var/mob/v in viewers(T)) v.show_message("\blue [M] presses \his thumb against [P].", 3, "\blue [P] makes a sharp clicking sound as it extracts DNA material from [M].", 2) var/datum/dna/dna = M.dna P << "

[M]'s UE string : [dna.unique_enzymes]

" if(dna.unique_enzymes == P.master_dna) P << "DNA is a match to stored Master DNA." else P << "DNA does not match stored Master DNA." else P << "[M] does not seem like \he is going to provide a DNA sample willingly." // -=-=-=-= Software =-=-=-=-=- // // Crew Manifest /mob/living/silicon/pai/proc/softwareManifest() var/dat = "" dat += "

Crew Manifest



" for (var/datum/data/record/t in data_core.general) dat += "[t.fields["name"]] - [t.fields["rank"]]
" return dat // Medical Records /mob/living/silicon/pai/proc/softwareMedicalRecord() var/dat = "" if(src.subscreen == 0) dat += "

Medical Records


" for(var/datum/data/record/R in data_core.general) dat += text("[]: []
", src, R, R.fields["id"], R.fields["name"]) //dat += text("
Back", src) if(src.subscreen == 1) dat += "
Medical Record

" if ((istype(src.medicalActive1, /datum/data/record) && data_core.general.Find(src.medicalActive1))) dat += text("Name: [] ID: []
\nSex: []
\nAge: []
\nFingerprint: []
\nPhysical Status: []
\nMental Status: []
", src.medicalActive1.fields["name"], src.medicalActive1.fields["id"], src.medicalActive1.fields["sex"], src.medicalActive1.fields["age"], src.medicalActive1.fields["fingerprint"], src.medicalActive1.fields["p_stat"], src.medicalActive1.fields["m_stat"]) else dat += "
Requested medical record not found.

" if ((istype(src.medicalActive2, /datum/data/record) && data_core.medical.Find(src.medicalActive2))) dat += text("
\n
Medical Data

\nBlood Type: []
\nDNA: []
\n
\nMinor Disabilities: []
\nDetails: []
\n
\nMajor Disabilities: []
\nDetails: []
\n
\nAllergies: []
\nDetails: []
\n
\nCurrent Diseases: [] (per disease info placed in log/comment section)
\nDetails: []
\n
\nImportant Notes:
\n\t[]
\n
\n
Comments/Log

", src, src.medicalActive2.fields["b_type"], src, src.medicalActive2.fields["b_dna"], src, src.medicalActive2.fields["mi_dis"], src, src.medicalActive2.fields["mi_dis_d"], src, src.medicalActive2.fields["ma_dis"], src, src.medicalActive2.fields["ma_dis_d"], src, src.medicalActive2.fields["alg"], src, src.medicalActive2.fields["alg_d"], src, src.medicalActive2.fields["cdi"], src, src.medicalActive2.fields["cdi_d"], src, src.medicalActive2.fields["notes"]) else dat += "
Requested medical record not found.

" dat += text("
\nBack
", src) return dat // Security Records /mob/living/silicon/pai/proc/softwareSecurityRecord() var/dat = "" if(src.subscreen == 0) dat += "

Security Records


" for(var/datum/data/record/R in data_core.general) dat += text("[]: []
", src, R, R.fields["id"], R.fields["name"]) if(src.subscreen == 1) dat += "

Security Record

" if ((istype(src.securityActive1, /datum/data/record) && data_core.general.Find(src.securityActive1))) dat += text("Name:
[] ID: []
\nSex: []
\nAge: []
\nRank: []
\nFingerprint: []
\nPhysical Status: []
\nMental Status: []
", src, src.securityActive1.fields["name"], src, src.securityActive1.fields["id"], src, src.securityActive1.fields["sex"], src, src.securityActive1.fields["age"], src, src.securityActive1.fields["rank"], src, src.securityActive1.fields["fingerprint"], src.securityActive1.fields["p_stat"], src.securityActive1.fields["m_stat"]) else dat += "
Requested security record not found,

" if ((istype(src.securityActive2, /datum/data/record) && data_core.security.Find(src.securityActive2))) dat += text("
\nSecurity Data
\nCriminal Status: []
\n
\nMinor Crimes: []
\nDetails: []
\n
\nMajor Crimes: []
\nDetails: []
\n
\nImportant Notes:
\n\t[]
\n
\n
Comments/Log

", src, src.securityActive2.fields["criminal"], src, src.securityActive2.fields["mi_crim"], src, src.securityActive2.fields["mi_crim_d"], src, src.securityActive2.fields["ma_crim"], src, src.securityActive2.fields["ma_crim_d"], src, src.securityActive2.fields["notes"]) else dat += "
Requested security record not found,

" dat += text("
\nBack
", src) return dat // Universal Translator /mob/living/silicon/pai/proc/softwareTranslator() var/dat = {"

Universal Translator


When enabled, this device will automatically convert all spoken and written language into a format that any known recipient can understand.

The device is currently [ (src.universal_speak) ? "en" : "dis" ]abled.
Toggle Device
"} return dat // Security HUD /mob/living/silicon/pai/proc/facialRecognition() var/dat = {"

Facial Recognition Suite


When enabled, this package will scan all viewable faces and compare them against the known criminal database, providing real-time graphical data about any detected persons of interest.

The package is currently [ (src.secHUD) ? "en" : "dis" ]abled.
Toggle Package
"} return dat // Medical HUD /mob/living/silicon/pai/proc/medicalAnalysis() var/dat = "" if(src.subscreen == 0) dat += {"

Medical Analysis Suite


Visual Status Overlay


When enabled, this package will scan all nearby crewmembers' vitals and provide real-time graphical data about their state of health.

The suite is currently [ (src.medHUD) ? "en" : "dis" ]abled.
Toggle Suite

Host Bioscan
"} if(src.subscreen == 1) dat += {"

Medical Analysis Suite


Host Bioscan


"} var/mob/living/M = src.loc if(!istype(M, /mob/living)) while (!istype(M, /mob/living)) M = M.loc if(istype(M, /turf)) src.temp = "Error: No biological host found.
" src.subscreen = 0 return dat dat += {"Bioscan Results for [M]:
" Overall Status: [M.stat > 1 ? "dead" : "[M.health]% healthy"]
Scan Breakdown:
Respiratory: [M.oxyloss > 50 ? "" : ""][M.oxyloss]
Toxicology: [M.toxloss > 50 ? "" : ""][M.toxloss]
Burns: [M.fireloss > 50 ? "" : ""][M.fireloss]
Structural Integrity: [M.bruteloss > 50 ? "" : ""][M.bruteloss]
Body Temperature: [M.bodytemperature-T0C]°C ([M.bodytemperature*1.8-459.67]°F)
"} for(var/datum/disease/D in M.viruses) dat += {"

Infection Detected.


Name: [D.name]
Type: [D.spread]
Stage: [D.stage]/[D.max_stages]
Possible Cure: [D.cure]
"} dat += "Visual Status Overlay
" return dat // Atmospheric Scanner /mob/living/silicon/pai/proc/softwareAtmo() var/dat = "

Atmospheric Sensor

" var/turf/T = get_turf_or_move(src.loc) if (isnull(T)) dat += "Unable to obtain a reading.
" else var/datum/gas_mixture/environment = T.return_air() var/pressure = environment.return_pressure() var/total_moles = environment.total_moles() dat += "Air Pressure: [round(pressure,0.1)] kPa
" if (total_moles) var/o2_level = environment.oxygen/total_moles var/n2_level = environment.nitrogen/total_moles var/co2_level = environment.carbon_dioxide/total_moles var/plasma_level = environment.toxins/total_moles var/unknown_level = 1-(o2_level+n2_level+co2_level+plasma_level) dat += "Nitrogen: [round(n2_level*100)]%
" dat += "Oxygen: [round(o2_level*100)]%
" dat += "Carbon Dioxide: [round(co2_level*100)]%
" dat += "Plasma: [round(plasma_level*100)]%
" if(unknown_level > 0.01) dat += "OTHER: [round(unknown_level)]%
" dat += "Temperature: [round(environment.temperature-T0C)]°C
" dat += "Refresh Reading
" dat += "
" return dat // Camera Jack - Clearly not finished /mob/living/silicon/pai/proc/softwareCamera() var/dat = "

Camera Jack

" dat += "Cable status : " if(!src.cable) dat += "Retracted
" return dat if(!src.cable.machine) dat += "Extended
" return dat var/obj/machinery/machine = src.cable.machine dat += "Connected
" if(!istype(machine, /obj/machinery/camera)) src << "DERP" return dat // Door Jack /mob/living/silicon/pai/proc/softwareDoor() var/dat = "

Airlock Jack

" dat += "Cable status : " if(!src.cable) dat += "Retracted
" dat += "Extend Cable
" return dat if(!src.cable.machine) dat += "Extended
" return dat var/obj/machinery/machine = src.cable.machine dat += "Connected
" if(!istype(machine, /obj/machinery/door)) dat += "Connected device's firmware does not appear to be compatible with Airlock Jack protocols.
" return dat // var/obj/machinery/airlock/door = machine if(!src.hackdoor) dat += "Begin Airlock Jacking
" else dat += "Jack in progress... [src.hackprogress]% complete.
" dat += "Cancel Airlock Jack
" //src.hackdoor = machine //src.hackloop() return dat // Door Jack - supporting proc /mob/living/silicon/pai/proc/hackloop() var/turf/T = get_turf_or_move(src.loc) for(var/mob/living/silicon/ai/AI in world) if(T.loc) AI << "Network Alert: Brute-force encryption crack in progress in [T.loc]." else AI << "Network Alert: Brute-force encryption crack in progress. Unable to pinpoint location." while(src.hackprogress < 100) if(src.cable && src.cable.machine && istype(src.cable.machine, /obj/machinery/door) && src.cable.machine == src.hackdoor && get_dist(src, src.hackdoor) <= 1) hackprogress += rand(1, 10) else src.temp = "Door Jack: Connection to airlock has been lost. Hack aborted." hackprogress = 0 src.hackdoor = null return if(hackprogress >= 100) // This is clunky, but works. We need to make sure we don't ever display a progress greater than 100, hackprogress = 100 // but we also need to reset the progress AFTER it's been displayed if(src.screen == "doorjack" && src.subscreen == 0) // Update our view, if appropriate src.paiInterface() if(hackprogress >= 100) src.hackprogress = 0 src.cable.machine:open() sleep(50) // Update every 5 seconds // Digital Messenger /mob/living/silicon/pai/proc/pdamessage() var/dat = "

Digital Messenger

" dat += "
    " for (var/obj/item/device/pda/P in world) if (!P.owner||P.toff||P == src) continue dat += "
  • [P]" dat += "
  • " for (var/mob/living/silicon/pai/P in world) if(P.stat != 2) dat += "
  • [P]" dat += "
  • " dat += "
" dat += "

" dat += "Messages:
[tnote]" return dat