From 538ee11cb19f0d286c1d4fa1f51ee89af68613f4 Mon Sep 17 00:00:00 2001 From: GinjaNinja32 Date: Mon, 9 Feb 2015 01:58:54 +0000 Subject: [PATCH 1/2] pAI rework --- baystation12.dme | 1 + code/game/objects/items/devices/paicard.dm | 3 + code/modules/mob/living/silicon/pai/pai.dm | 5 +- .../mob/living/silicon/pai/software.dm | 836 +++--------------- .../living/silicon/pai/software_modules.dm | 454 ++++++++++ code/modules/nano/nanoui.dm | 6 + nano/templates/pai_atmosphere.tmpl | 36 + nano/templates/pai_directives.tmpl | 50 ++ nano/templates/pai_doorjack.tmpl | 44 + nano/templates/pai_interface.tmpl | 46 + nano/templates/pai_manifest.tmpl | 76 ++ nano/templates/pai_medrecords.tmpl | 86 ++ nano/templates/pai_radio.tmpl | 48 + nano/templates/pai_secrecords.tmpl | 76 ++ nano/templates/pai_signaller.tmpl | 33 + 15 files changed, 1088 insertions(+), 712 deletions(-) mode change 100755 => 100644 code/modules/mob/living/silicon/pai/pai.dm mode change 100755 => 100644 code/modules/mob/living/silicon/pai/software.dm create mode 100644 code/modules/mob/living/silicon/pai/software_modules.dm create mode 100644 nano/templates/pai_atmosphere.tmpl create mode 100644 nano/templates/pai_directives.tmpl create mode 100644 nano/templates/pai_doorjack.tmpl create mode 100644 nano/templates/pai_interface.tmpl create mode 100644 nano/templates/pai_manifest.tmpl create mode 100644 nano/templates/pai_medrecords.tmpl create mode 100644 nano/templates/pai_radio.tmpl create mode 100644 nano/templates/pai_secrecords.tmpl create mode 100644 nano/templates/pai_signaller.tmpl diff --git a/baystation12.dme b/baystation12.dme index 3682d724958..f85957dbc8f 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -1147,6 +1147,7 @@ #include "code\modules\mob\living\silicon\pai\recruit.dm" #include "code\modules\mob\living\silicon\pai\say.dm" #include "code\modules\mob\living\silicon\pai\software.dm" +#include "code\modules\mob\living\silicon\pai\software_modules.dm" #include "code\modules\mob\living\silicon\robot\analyzer.dm" #include "code\modules\mob\living\silicon\robot\component.dm" #include "code\modules\mob\living\silicon\robot\death.dm" diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index a938d6b9774..d3c667b9aa2 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -281,6 +281,8 @@ src.overlays.Cut() src.overlays += "pai-off" +/obj/item/device/paicard + var/current_emotion = 1 /obj/item/device/paicard/proc/setEmotion(var/emotion) if(pai) src.overlays.Cut() @@ -294,6 +296,7 @@ if(7) src.overlays += "pai-sad" if(8) src.overlays += "pai-angry" if(9) src.overlays += "pai-what" + current_emotion = emotion /obj/item/device/paicard/proc/alertUpdate() var/turf/T = get_turf_or_move(src.loc) diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm old mode 100755 new mode 100644 index a334809a370..bcca7b5bdb9 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -53,14 +53,17 @@ var/secHUD = 0 // Toggles whether the Security HUD is active or not var/medHUD = 0 // Toggles whether the Medical HUD is active or not + var/medical_cannotfind = 0 var/datum/data/record/medicalActive1 // Datacore record declarations for record software var/datum/data/record/medicalActive2 + var/security_cannotfind = 0 var/datum/data/record/securityActive1 // Could probably just combine all these into one var/datum/data/record/securityActive2 var/obj/machinery/door/hackdoor // The airlock being hacked - var/hackprogress = 0 // Possible values: 0 - 100, >= 100 means the hack is complete and will be reset upon next check + var/hackprogress = 0 // Possible values: 0 - 1000, >= 1000 means the hack is complete and will be reset upon next check + var/hack_aborted = 0 var/obj/item/radio/integrated/signal/sradio // AI's signaller diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm old mode 100755 new mode 100644 index 68cfdabad1a..cb9431f04e7 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -1,711 +1,125 @@ -// 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 - "remote signaller" = 5, - ) - -/mob/living/silicon/pai/verb/paiInterface() - set category = "pAI Commands" - set name = "Software Interface" - var/dat = "" - var/left_part = "" - var/right_part = softwareMenu() - src.set_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ñ" //This file has to be saved as ANSI or this will not display correctly - 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() - if("signaller") - left_part = src.softwareSignal() - if("radio") - left_part = src.softwareRadio() - - //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=685x449;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 && !sub) - return - 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") - if(href_list["freq"]) - var/new_frequency = (radio.frequency + text2num(href_list["freq"])) - if(new_frequency < 1441 || new_frequency > 1599) - new_frequency = sanitize_frequency(new_frequency) - else - radio.set_frequency(new_frequency) - else if (href_list["talk"]) - radio.broadcasting = text2num(href_list["talk"]) - else if (href_list["listen"]) - radio.listening = text2num(href_list["listen"]) - - if("image") - var/newImage = input("Select your new display image.", "Display Image", "Happy") in list("Happy", "Cat", "Extremely Happy", "Face", "Laugh", "Off", "Sad", "Angry", "What") - var/pID = 1 - - switch(newImage) - if("Happy") - pID = 1 - if("Cat") - pID = 2 - if("Extremely Happy") - pID = 3 - if("Face") - pID = 4 - if("Laugh") - pID = 5 - if("Off") - pID = 6 - if("Sad") - pID = 7 - if("Angry") - pID = 8 - if("What") - pID = 9 - src.card.setEmotion(pID) - - if("signaller") - - if(href_list["send"]) - - sradio.send_signal("ACTIVATE") - for(var/mob/O in hearers(1, src.loc)) - O.show_message(text("\icon[] *beep* *beep*", src), 3, "*beep* *beep*", 2) - - if(href_list["freq"]) - - var/new_frequency = (sradio.frequency + text2num(href_list["freq"])) - if(new_frequency < 1200 || new_frequency > 1600) - new_frequency = sanitize_frequency(new_frequency) - sradio.set_frequency(new_frequency) - - if(href_list["code"]) - - sradio.code += text2num(href_list["code"]) - sradio.code = round(sradio.code) - sradio.code = min(100, sradio.code) - sradio.code = max(1, sradio.code) - - - - if("directive") - if(href_list["getdna"]) - var/mob/living/M = src.loc - var/count = 0 - while(!istype(M, /mob/living)) - if(!M || !M.loc) return 0 //For a runtime where M ends up in nullspace (similar to bluespace but less colourful) - M = M.loc - count++ - if(count >= 6) - src << "You are not being carried by anyone!" - return 0 - spawn CheckDNA(M, src) - - if("pdamessage") - if(!isnull(pda)) - if(href_list["toggler"]) - pda.toff = !pda.toff - else if(href_list["ringer"]) - pda.message_silent = !pda.message_silent - else if(href_list["target"]) - if(silence_time) - return alert("Communications circuits remain uninitialized.") - - var/target = locate(href_list["target"]) - pda.create_message(src, target, 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.translator_toggle() - 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 - -// MENUS - -/mob/living/silicon/pai/proc/softwareMenu() // Populate the right menu - var/dat = "" - - dat += "Refresh
" - // Built-in - dat += "Directives
" - dat += "Radio Configuration
" - dat += "Screen Display
" - //dat += "Text Messaging
" - dat += "
" - - // Basic - dat += "Basic
" - for(var/s in src.software) - if(s == "digital messenger") - dat += "Digital Messenger [(pda.toff) ? "" : ""]
" - 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
" - if(s == "remote signaller") - dat += "Remote Signaller
" - 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") //This file has to be saved as ANSI or this will not display correctly - dat += "Facial Recognition Suite [(src.secHUD) ? "" : ""]
" - if(s == "medical HUD") //This file has to be saved as ANSI or this will not display correctly - dat += "Medical Analysis Suite [(src.medHUD) ? "" : ""]
" - if(s == "universal translator") //This file has to be saved as ANSI or this will not display correctly - dat += "Universal Translator [(src.translator_on) ? "" : ""]
" - 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

" - 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 =-=-=-=- // - -//Remote Signaller -/mob/living/silicon/pai/proc/softwareSignal() - var/dat = "" - dat += "

Remote Signaller


" - dat += {"Frequency/Code for signaler:
- Frequency: - - - - - [format_frequency(src.sradio.frequency)] - + - +
- - Code: - - - - - [src.sradio.code] - + - +
- - Send Signal
"} - return dat - -//Station Bounced Radio -/mob/living/silicon/pai/proc/softwareRadio() - var/dat = "" - dat += "

Station Bounced Radio


" - if(!istype(src, /obj/item/device/radio/headset)) //Headsets don't get a mic button - dat += "Microphone: [radio.broadcasting ? "Engaged" : "Disengaged"]
" - dat += {" - Speaker: [radio.listening ? "Engaged" : "Disengaged"]
- Frequency: - - - - - [format_frequency(radio.frequency)] - + - +
- "} - - for (var/ch_name in radio.channels) - dat+=radio.text_sec_channel(ch_name, radio.channels[ch_name]) - dat+={"[radio.text_wires()]"} - - return dat - -// Crew Manifest -/mob/living/silicon/pai/proc/softwareManifest() - var/dat = "" - dat += "

Crew Manifest


" - if(data_core) - dat += data_core.get_manifest(0) // make it monochrome - dat += "
" - return dat - -// Medical Records -/mob/living/silicon/pai/proc/softwareMedicalRecord() - var/dat = "" - if(src.subscreen == 0) - dat += "

Medical Records


" - if(!isnull(data_core.general)) - for(var/datum/data/record/R in sortRecord(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: []
\nID: []
\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


" - if(!isnull(data_core.general)) - for(var/datum/data/record/R in sortRecord(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:
[]
\nID: []
\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.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 languages into a format that any known recipient can understand.

- The device is currently [ (src.translator_on) ? "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 suite is currently [ (src.secHUD) ? "en" : "dis" ]abled.
- Toggle Suite
- "} - 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 - //If we are not deployed, check the holder of the card. - if(src.loc == card) - M = card.loc - - //If we are deployed or the card is not held, check the first living mob in our turf. - if(!M || !istype(M)) - var/turf/T = get_turf(src) - M = locate(/mob/living/) in T.contents - - if(!M || !istype(M)) - 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.getOxyLoss() > 50 ? "" : ""][M.getOxyLoss()]
- Toxicology: [M.getToxLoss() > 50 ? "" : ""][M.getToxLoss()]
- Burns: [M.getFireLoss() > 50 ? "" : ""][M.getFireLoss()]
- Structural Integrity: [M.getBruteLoss() > 50 ? "" : ""][M.getBruteLoss()]
- 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 += "
Refresh Bioscan
" - 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) - for(var/g in environment.gas) - dat += "[gas_data.name[g]]: [round((environment.gas[g] / total_moles) * 100)]%
" - dat += "Temperature: [round(environment.temperature-T0C)]°C
" - dat += "
Refresh Reading" - 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 player_list) - 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 += {"Signal/Receiver Status: - [(pda.toff) ? " \[Off\]" : " \[On\]"]
- Ringer Status: - [(pda.message_silent) ? " \[Off\]" : " \[On\]"]

"} - dat += "
    " - if(!pda.toff) - for (var/obj/item/device/pda/P in sortAtom(PDAs)) - if (!P.owner||P.toff||P == src.pda||P.hidden) continue - dat += "
  • [P]" - dat += "
  • " - dat += "
" - dat += "Messages:
" - - dat += "" - dat += "" - for(var/index in pda.tnote) - if(index["sent"]) - dat += addtext("") - else - dat += addtext("") - dat += "
To", index["owner"],": ", index["message"], "
From", index["owner"],": ", index["message"], "
" - return dat - -/mob/living/silicon/pai/proc/translator_toggle() - - // Sol Common, Tradeband and Gutter are added with New() and are therefore the current default, always active languages - - if(translator_on) - translator_on = 0 - - remove_language("Sinta'unathi") - remove_language("Siik'tajr") - remove_language("Skrellian") - - src << "\blue Translator Module toggled OFF." - - else - translator_on = 1 - - add_language("Sinta'unathi") - add_language("Siik'tajr") - add_language("Skrellian") - - src << "\blue Translator Module toggled ON." +var/list/pai_emotions = list( + "Happy" = 1, + "Cat" = 2, + "Extremely Happy" = 3, + "Face" = 4, + "Laugh" = 5, + "Off" = 6, + "Sad" = 7, + "Angry" = 8, + "What" = 9 + ) + + +var/global/list/pai_software_by_key = list() +var/global/list/default_pai_software = list() +/hook/startup/proc/populate_pai_software_list() + var/r = 1 // I would use ., but it'd sacrifice runtime detection + for(var/type in typesof(/datum/pai_software) - /datum/pai_software) + var/datum/pai_software/P = new type() + if(pai_software_by_key[P.id]) + var/datum/pai_software/O = pai_software_by_key[P.id] + world << "pAI software module [P.name] has the same key as [O.name]!" + r = 0 + continue + pai_software_by_key[P.id] = P + if(P.default) + default_pai_software[P.id] = P + return r + +/mob/living/silicon/pai/New() + ..() + software = default_pai_software.Copy() + +/mob/living/silicon/pai/verb/paiInterface() + set category = "pAI Commands" + set name = "Software Interface" + + ui_interact(src) + +/mob/living/silicon/pai/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) + if(user != src) + if(ui) ui.set_status(STATUS_CLOSE, 0) + return + + if(ui_key != "main") + var/datum/pai_software/S = software[ui_key] + if(S && !S.toggle) + S.on_ui_interact(src, ui, force_open) + else + if(ui) ui.set_status(STATUS_CLOSE, 0) + return + + var/data[0] + + // Software we have bought + var/bought_software[0] + // Software we have not bought + var/not_bought_software[0] + + for(var/key in pai_software_by_key) + var/datum/pai_software/S = pai_software_by_key[key] + var/software_data[0] + software_data["name"] = S.name + software_data["id"] = S.id + if(key in software) + software_data["on"] = S.is_active(src) + bought_software[++bought_software.len] = software_data + else + software_data["ram"] = S.ram_cost + not_bought_software[++not_bought_software.len] = software_data + + data["bought"] = bought_software + data["not_bought"] = not_bought_software + data["available_ram"] = ram + + // Emotions + var/emotions[0] + for(var/name in pai_emotions) + var/emote[0] + emote["name"] = name + emote["id"] = pai_emotions[name] + emotions[++emotions.len] = emote + + data["emotions"] = emotions + data["current_emotion"] = card.current_emotion + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "pai_interface.tmpl", "pAI Software Interface", 450, 600) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/mob/living/silicon/pai/Topic(href, href_list) + . = ..() + if(.) return + + if(href_list["software"]) + var/soft = href_list["software"] + var/datum/pai_software/S = software[soft] + if(S.toggle) + S.toggle(src) + else + ui_interact(src, ui_key = soft) + return 1 + + else if(href_list["stopic"]) + var/soft = href_list["stopic"] + var/datum/pai_software/S = software[soft] + if(S) + return S.Topic(href, href_list) + + else if(href_list["purchase"]) + var/soft = href_list["purchase"] + var/datum/pai_software/S = pai_software_by_key[soft] + if(S && (ram >= S.ram_cost)) + ram -= S.ram_cost + software[S.id] = S + return 1 + + else if(href_list["image"]) + var/img = text2num(href_list["image"]) + if(1 <= img && img <= 9) + card.setEmotion(img) + return 1 diff --git a/code/modules/mob/living/silicon/pai/software_modules.dm b/code/modules/mob/living/silicon/pai/software_modules.dm new file mode 100644 index 00000000000..dcb38e5efdd --- /dev/null +++ b/code/modules/mob/living/silicon/pai/software_modules.dm @@ -0,0 +1,454 @@ +/datum/pai_software + // Name for the software. This is used as the button text when buying or opening/toggling the software + var/name = "pAI software module" + // RAM cost; pAIs start with 100 RAM, spending it on programs + var/ram_cost = 0 + // ID for the software. This must be unique + var/id = "" + // Whether this software is a toggle or not + // Toggled software should override toggle() and is_active() + // Non-toggled software should override on_ui_interact() and Topic() + var/toggle = 1 + // Whether pAIs should automatically receive this module at no cost + var/default = 0 + + proc/on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) + return + + proc/toggle(mob/living/silicon/pai/user) + return + + proc/is_active(mob/living/silicon/pai/user) + return 0 + +/datum/pai_software/directives + name = "Directives" + ram_cost = 0 + id = "directives" + toggle = 0 + default = 1 + + on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) + var/data[0] + + data["master"] = user.master + data["dna"] = user.master_dna + data["prime"] = user.pai_law0 + data["supplemental"] = user.pai_laws + + ui = nanomanager.try_update_ui(user, user, id, ui, data, force_open) + if(!ui) + // Don't copy-paste this unless you're making a pAI software module! + ui = new(user, user, id, "pai_directives.tmpl", "pAI Directives", 450, 600) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + + Topic(href, href_list) + var/mob/living/silicon/pai/P = usr + if(!istype(P)) return + + if(href_list["getdna"]) + var/mob/living/M = P.loc + var/count = 0 + + // Find the carrier + while(!istype(M, /mob/living)) + if(!M || !M.loc || count > 6) + //For a runtime where M ends up in nullspace (similar to bluespace but less colourful) + src << "You are not being carried by anyone!" + return 0 + M = M.loc + count++ + + // Check the carrier + 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." + return 1 + +/datum/pai_software/radio_config + name = "Radio Configuration" + ram_cost = 0 + id = "radio" + toggle = 0 + default = 1 + + on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui = null, force_open = 1) + var/data[0] + + data["listening"] = user.radio.broadcasting + data["frequency"] = format_frequency(user.radio.frequency) + + var/channels[0] + for(var/ch_name in user.radio.channels) + var/ch_stat = user.radio.channels[ch_name] + var/ch_dat[0] + ch_dat["name"] = ch_name + // FREQ_LISTENING is const in /obj/item/device/radio + ch_dat["listening"] = !!(ch_stat & user.radio.FREQ_LISTENING) + channels[++channels.len] = ch_dat + + data["channels"] = channels + + ui = nanomanager.try_update_ui(user, user, id, ui, data, force_open) + if(!ui) + ui = new(user, user, id, "pai_radio.tmpl", "Radio Configuration", 300, 150) + ui.set_initial_data(data) + ui.open() + + Topic(href, href_list) + var/mob/living/silicon/pai/P = usr + if(!istype(P)) return + + P.radio.Topic(href, href_list) + return 1 + +/datum/pai_software/crew_manifest + name = "Crew Manifest" + ram_cost = 5 + id = "manifest" + toggle = 0 + + on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) + data_core.get_manifest_json() + + var/data[0] + // This is dumb, but NanoUI breaks if it has no data to send + data["a"] = "a" + + if(ui) + ui.load_cached_data(ManifestJSON) + + ui = nanomanager.try_update_ui(user, user, id, ui, data, force_open) + if(!ui) + // Don't copy-paste this unless you're making a pAI software module! + ui = new(user, user, id, "pai_manifest.tmpl", "Crew Manifest", 450, 600) + ui.load_cached_data(ManifestJSON) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/datum/pai_software/messenger + name = "Digital Messenger" + ram_cost = 5 + id = "messenger" + toggle = 0 + +/datum/pai_software/med_records + name = "Medical Records" + ram_cost = 15 + id = "med_records" + toggle = 0 + + on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) + var/data[0] + + var/records[0] + for(var/datum/data/record/general in sortRecord(data_core.general)) + var/record[0] + record["name"] = general.fields["name"] + record["ref"] = "\ref[general]" + records[++records.len] = record + + data["records"] = records + + var/datum/data/record/G = user.medicalActive1 + var/datum/data/record/M = user.medicalActive2 + data["general"] = G ? G.fields : null + data["medical"] = M ? M.fields : null + data["could_not_find"] = user.medical_cannotfind + + ui = nanomanager.try_update_ui(user, user, id, ui, data, force_open) + if(!ui) + // Don't copy-paste this unless you're making a pAI software module! + ui = new(user, user, id, "pai_medrecords.tmpl", "Medical Records", 450, 600) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + + Topic(href, href_list) + var/mob/living/silicon/pai/P = usr + if(!istype(P)) return + + if(href_list["select"]) + var/datum/data/record/record = locate(href_list["select"]) + if(record) + var/datum/data/record/R = record + var/datum/data/record/M = null + if (!( data_core.general.Find(R) )) + P.medical_cannotfind = 1 + else + P.medical_cannotfind = 0 + 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 + P.medicalActive1 = R + P.medicalActive2 = M + else + P.medical_cannotfind = 1 + return 1 + +/datum/pai_software/sec_records + name = "Security Records" + ram_cost = 15 + id = "sec_records" + toggle = 0 + + on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) + var/data[0] + + var/records[0] + for(var/datum/data/record/general in sortRecord(data_core.general)) + var/record[0] + record["name"] = general.fields["name"] + record["ref"] = "\ref[general]" + records[++records.len] = record + + data["records"] = records + + var/datum/data/record/G = user.securityActive1 + var/datum/data/record/S = user.securityActive2 + data["general"] = G ? G.fields : null + data["security"] = S ? S.fields : null + data["could_not_find"] = user.medical_cannotfind + + ui = nanomanager.try_update_ui(user, user, id, ui, data, force_open) + if(!ui) + // Don't copy-paste this unless you're making a pAI software module! + ui = new(user, user, id, "pai_secrecords.tmpl", "Security Records", 450, 600) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + + Topic(href, href_list) + var/mob/living/silicon/pai/P = usr + if(!istype(P)) return + + if(href_list["select"]) + var/datum/data/record/record = locate(href_list["select"]) + if(record) + var/datum/data/record/R = record + var/datum/data/record/S = null + if (!( data_core.general.Find(R) )) + P.medical_cannotfind = 1 + else + P.medical_cannotfind = 0 + for(var/datum/data/record/E in data_core.security) + if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) + S = E + P.securityActive1 = R + P.securityActive2 = S + else + P.security_cannotfind = 1 + return 1 + +/datum/pai_software/door_jack + name = "Door Jack" + ram_cost = 30 + id = "door_jack" + toggle = 0 + + on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) + var/data[0] + + data["cable"] = user.cable != null + data["machine"] = user.cable && (user.cable.machine != null) + data["inprogress"] = user.hackdoor != null + data["progress_a"] = round(user.hackprogress / 10) + data["progress_b"] = user.hackprogress % 10 + data["aborted"] = user.hack_aborted + + ui = nanomanager.try_update_ui(user, user, id, ui, data, force_open) + if(!ui) + // Don't copy-paste this unless you're making a pAI software module! + ui = new(user, user, id, "pai_doorjack.tmpl", "Door Jack", 300, 150) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + + Topic(href, href_list) + var/mob/living/silicon/pai/P = usr + if(!istype(P)) return + + if(href_list["jack"]) + if(P.cable && P.cable.machine) + P.hackdoor = P.cable.machine + P.hackloop() + return 1 + else if(href_list["cancel"]) + P.hackdoor = null + return 1 + else if(href_list["cable"]) + var/turf/T = get_turf_or_move(P.loc) + P.hack_aborted = 0 + P.cable = new /obj/item/weapon/pai_cable(T) + for(var/mob/M in viewers(T)) + M.show_message("A port on [P] opens to reveal [P.cable], which promptly falls to the floor.", 3, + "You hear the soft click of something light and hard falling to the ground.", 2) + return 1 + +/mob/living/silicon/pai/proc/hackloop() + var/turf/T = get_turf_or_move(src.loc) + for(var/mob/living/silicon/ai/AI in player_list) + 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." + var/obj/machinery/door/D = cable.machine + if(!istype(D)) + hack_aborted = 1 + hackprogress = 0 + cable.machine = null + hackdoor = null + return + while(hackprogress < 1000) + if(cable && cable.machine == D && cable.machine == hackdoor && get_dist(src, hackdoor) <= 1) + hackprogress = min(hackprogress+rand(1, 20), 1000) + else + hack_aborted = 1 + hackprogress = 0 + hackdoor = null + return + if(hackprogress >= 1000) + hackprogress = 0 + D.open() + cable.machine = null + return + sleep(10) // Update every second + +/datum/pai_software/atmosphere_sensor + name = "Atmosphere Sensor" + ram_cost = 5 + id = "atmos_sense" + toggle = 0 + + on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) + var/data[0] + + var/turf/T = get_turf_or_move(user.loc) + if(!T) + data["reading"] = 0 + data["pressure"] = 0 + data["temperature"] = 0 + data["temperatureC"] = 0 + data["gas"] = list() + else + var/datum/gas_mixture/env = T.return_air() + data["reading"] = 1 + var/pres = env.return_pressure() * 10 + data["pressure"] = "[round(pres/10)].[pres%10]" + data["temperature"] = round(env.temperature) + data["temperatureC"] = round(env.temperature-T0C) + + var/t_moles = env.total_moles + var/gases[0] + for(var/g in env.gas) + var/gas[0] + gas["name"] = gas_data.name[g] + gas["percent"] = round((env.gas[g] / t_moles) * 100) + gases[++gases.len] = gas + data["gas"] = gases + + ui = nanomanager.try_update_ui(user, user, id, ui, data, force_open) + if(!ui) + // Don't copy-paste this unless you're making a pAI software module! + ui = new(user, user, id, "pai_atmosphere.tmpl", "Atmosphere Sensor", 350, 300) + ui.set_initial_data(data) + ui.open() + +/datum/pai_software/sec_hud + name = "Security HUD" + ram_cost = 20 + id = "sec_hud" + + toggle(mob/living/silicon/pai/user) + user.secHUD = !user.secHUD + + is_active(mob/living/silicon/pai/user) + return user.secHUD + +/datum/pai_software/med_hud + name = "Medical HUD" + ram_cost = 20 + id = "med_hud" + + toggle(mob/living/silicon/pai/user) + user.medHUD = !user.medHUD + + is_active(mob/living/silicon/pai/user) + return user.medHUD + +/datum/pai_software/translator + name = "Universal Translator" + ram_cost = 35 + id = "translator" + + toggle(mob/living/silicon/pai/user) + // Sol Common, Tradeband and Gutter are added with New() and are therefore the current default, always active languages + user.translator_on = !user.translator_on + if(user.translator_on) + user.add_language("Sinta'unathi") + user.add_language("Siik'tajr") + user.add_language("Skrellian") + else + user.remove_language("Sinta'unathi") + user.remove_language("Siik'tajr") + user.remove_language("Skrellian") + + is_active(mob/living/silicon/pai/user) + return user.translator_on + +/datum/pai_software/signaller + name = "Remote Signaller" + ram_cost = 5 + id = "signaller" + toggle = 0 + + on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) + var/data[0] + + data["frequency"] = format_frequency(user.sradio.frequency) + data["code"] = user.sradio.code + + ui = nanomanager.try_update_ui(user, user, id, ui, data, force_open) + if(!ui) + // Don't copy-paste this unless you're making a pAI software module! + ui = new(user, user, id, "pai_signaller.tmpl", "Signaller", 320, 150) + ui.load_cached_data(ManifestJSON) + ui.set_initial_data(data) + ui.open() + + Topic(href, href_list) + var/mob/living/silicon/pai/P = usr + if(!istype(P)) return + + if(href_list["send"]) + P.sradio.send_signal("ACTIVATE") + for(var/mob/O in hearers(1, P.loc)) + O.show_message(text("\icon[] *beep* *beep*", P), 3, "*beep* *beep*", 2) + return 1 + + else if(href_list["freq"]) + var/new_frequency = (P.sradio.frequency + text2num(href_list["freq"])) + if(new_frequency < 1200 || new_frequency > 1600) + new_frequency = sanitize_frequency(new_frequency) + P.sradio.set_frequency(new_frequency) + return 1 + + else if(href_list["code"]) + P.sradio.code += text2num(href_list["code"]) + P.sradio.code = round(P.sradio.code) + P.sradio.code = min(100, P.sradio.code) + P.sradio.code = max(1, P.sradio.code) + return 1 diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm index a2830661576..fbd809cd03c 100644 --- a/code/modules/nano/nanoui.dm +++ b/code/modules/nano/nanoui.dm @@ -142,6 +142,12 @@ nanoui is used to open and update nano browser uis Procs called by update_status() */ +/mob/living/silicon/pai/can_interact_with_interface(src_object) + if(src_object == src && !stat) + return STATUS_INTERACTIVE + else + return ..() + /mob/proc/can_interact_with_interface(var/src_object) return STATUS_CLOSE // By default no mob can do anything with NanoUI diff --git a/nano/templates/pai_atmosphere.tmpl b/nano/templates/pai_atmosphere.tmpl new file mode 100644 index 00000000000..898bf7bc453 --- /dev/null +++ b/nano/templates/pai_atmosphere.tmpl @@ -0,0 +1,36 @@ + + +{{if data.reading}} +
+
+ Pressure: +
+
+ {{:data.pressure}} kPa +
+
+ +
+
+ Temperature: +
+
+ {{:data.temperature}}K ({{:data.temperatureC}}°C) +
+
+ + {{for data.gas}} +
+
+ {{:value.name}}: +
+
+ {{:value.percent}}% +
+
+ {{/for}} +{{else}} + Unable to obtain air reading! +{{/if}} diff --git a/nano/templates/pai_directives.tmpl b/nano/templates/pai_directives.tmpl new file mode 100644 index 00000000000..2caff849a26 --- /dev/null +++ b/nano/templates/pai_directives.tmpl @@ -0,0 +1,50 @@ + + +
+
+ Master: +
+
+ {{if data.master}} + {{:data.master}} ({{:data.dna}}) + {{:helper.link("Request carrier DNA sample", '', {"stopic":"directives", "getdna":1})}} + {{else}} + None + {{/if}} +
+
+ +
+
+ Prime Directive: +
+
+ {{:data.prime}} +
+
+ +
+
+ Supplemental Directive(s): +
+
+ {{if data.supplemental}} + {{:data.supplemental}} + {{else}} + None + {{/if}} +
+
+ +
+ + 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. + +
\ No newline at end of file diff --git a/nano/templates/pai_doorjack.tmpl b/nano/templates/pai_doorjack.tmpl new file mode 100644 index 00000000000..6862663b9b9 --- /dev/null +++ b/nano/templates/pai_doorjack.tmpl @@ -0,0 +1,44 @@ + + +
+
+ Cable: +
+
+ {{if data.machine}} + {{:helper.link("Connected", '', {}, 'selected')}} + {{else data.cable}} + {{:helper.link("Extended", '', {}, 'yellowButton')}} + {{else}} + {{:helper.link("Retracted", '', {"stopic":"door_jack", "cable":1})}} + {{/if}} +
+
+ +{{if data.machine}} +
+
+ Hack: +
+
+ {{if data.inprogress}} + {{if data.progress_a <= 33}} +
{{:data.progress_a}}.{{:data.progress_b}}%
+ {{else data.progress_a <= 67}} +
{{:data.progress_a}}.{{:data.progress_b}}%
+ {{else}} +
{{:data.progress_a}}.{{:data.progress_b}}%
+ {{/if}} + {{:helper.link("Cancel", '', {"stopic":"door_jack", "cancel":1}, null, 'redButton')}} + {{else}} + {{:helper.link("Start", '', {"stopic":"door_jack", "jack":1})}} + {{/if}} +
+
+{{else data.aborted}} +
+
Hack aborted!
+
+{{/if}} \ No newline at end of file diff --git a/nano/templates/pai_interface.tmpl b/nano/templates/pai_interface.tmpl new file mode 100644 index 00000000000..27c8a9fcf7f --- /dev/null +++ b/nano/templates/pai_interface.tmpl @@ -0,0 +1,46 @@ + + + + + Emotion: + + + {{for data.emotions}} + {{:helper.link(value.name, '', {"image" : value.id}, data.current_emotion == value.id ? 'selected' : null)}} + {{/for}} + + + + + + Installed Software: + + + {{for data.bought}} + + {{:helper.link(value.name, '', {"software" : value.id}, null, value.on ? 'selected' : null)}} + {{/for}} + + + + + + Available RAM: + + + {{:data.available_ram}} + + + + + + Downloadable Software: + + + {{for data.not_bought}} + {{:helper.link(value.name + " (" + value.ram + ")", '', {"purchase" : value.id}, value.ram <= data.available_ram ? null : 'disabled')}} + {{/for}} + + \ No newline at end of file diff --git a/nano/templates/pai_manifest.tmpl b/nano/templates/pai_manifest.tmpl new file mode 100644 index 00000000000..0ab49c02fce --- /dev/null +++ b/nano/templates/pai_manifest.tmpl @@ -0,0 +1,76 @@ + + +
+
+ {{if data.cached.heads.length}} + + {{for data.cached["heads"]}} + {{if value.rank == "Captain"}} + + {{else}} + + {{/if}} + {{/for}} + {{/if}} + {{if data.cached.sec.length}} + + {{for data.cached["sec"]}} + {{if value.rank == "Head of Security"}} + + {{else}} + + {{/if}} + {{/for}} + {{/if}} + {{if data.cached.eng.length}} + + {{for data.cached["eng"]}} + {{if value.rank == "Chief Engineer"}} + + {{else}} + + {{/if}} + {{/for}} + {{/if}} + {{if data.cached.med.length}} + + {{for data.cached["med"]}} + {{if value.rank == "Chief Medical Officer"}} + + {{else}} + + {{/if}} + {{/for}} + {{/if}} + {{if data.cached.sci.length}} + + {{for data.cached["sci"]}} + {{if value.rank == "Research Director"}} + + {{else}} + + {{/if}} + {{/for}} + {{/if}} + {{if data.cached.civ.length}} + + {{for data.cached["civ"]}} + {{if value.rank == "Head of Personnel"}} + + {{else}} + + {{/if}} + {{/for}} + {{/if}} + {{if data.cached.misc.length}} + + {{for data.cached["misc"]}} + + {{/for}} + {{/if}} +
Command
{{:value.name}}{{:value.rank}}{{:value.active}}
{{:value.name}}{{:value.rank}}{{:value.active}}
Security
{{:value.name}}{{:value.rank}}{{:value.active}}
{{:value.name}}{{:value.rank}}{{:value.active}}
Engineering
{{:value.name}}{{:value.rank}}{{:value.active}}
{{:value.name}}{{:value.rank}}{{:value.active}}
Medical
{{:value.name}}{{:value.rank}}{{:value.active}}
{{:value.name}}{{:value.rank}}{{:value.active}}
Science
{{:value.name}}{{:value.rank}}{{:value.active}}
{{:value.name}}{{:value.rank}}{{:value.active}}
Civilian
{{:value.name}}{{:value.rank}}{{:value.active}}
{{:value.name}}{{:value.rank}}{{:value.active}}
Misc
{{:value.name}}{{:value.rank}}{{:value.active}}
+
\ No newline at end of file diff --git a/nano/templates/pai_medrecords.tmpl b/nano/templates/pai_medrecords.tmpl new file mode 100644 index 00000000000..37b1d7061ca --- /dev/null +++ b/nano/templates/pai_medrecords.tmpl @@ -0,0 +1,86 @@ + + +{{if data.records}} + {{for data.records}} +
+ {{:helper.link(value.name, '', {"stopic":"med_records", "select":value.ref})}} +
+ {{/for}} +{{/if}} +
+{{if data.general}} +
+
Name
+
{{:data.general.name}}
+
+
+
Record ID
+
{{:data.general.id}}
+
+
+
Sex
+
{{:data.general.sex}}
+
+
+
Species
+
{{:data.general.species}}
+
+
+
Age
+
{{:data.general.age}}
+
+
+
Rank
+
{{:data.general.rank}}
+
+
+
Fingerprint
+
{{:data.general.fingerprint}}
+
+
+
Physical Status
+
{{:data.general.p_stat}}
+
+
+
Mental Status
+
{{:data.general.m_stat}}
+
+{{/if}} +{{if data.medical}} +
+
Blood Type
+
{{:data.medical.b_type}}
+
+
+
Minor Disabilities
+
{{:data.medical.mi_dis}}
+
{{:data.medical.mi_dis_d}}
+
+
+
Major Disabilities
+
{{:data.medical.ma_dis}}
+
{{:data.medical.ma_dis_d}}
+
+
+
Allergies
+
{{:data.medical.alg}}
+
{{:data.medical.alg_d}}
+
+
+
Current Diseases
+
{{:data.medical.cdi}}
+
{{:data.medical.cdi_d}}
+
+
+
Important Notes
+
{{:data.medical.notes}}
+
+{{/if}} + +{{if data.could_not_find}} +
+ Failed to find some records; the information above may be incomplete or missing. +
+{{/if}} \ No newline at end of file diff --git a/nano/templates/pai_radio.tmpl b/nano/templates/pai_radio.tmpl new file mode 100644 index 00000000000..bcdacdb48f1 --- /dev/null +++ b/nano/templates/pai_radio.tmpl @@ -0,0 +1,48 @@ + + +
+
+ Microphone: +
+
+ {{if data.listening}} + {{:helper.link("On", '', {"stopic":"radio", "talk":1}, 'selected')}} + {{:helper.link("Off", '', {"stopic":"radio", "talk":1})}} + {{else}} + {{:helper.link("On", '', {"stopic":"radio", "talk":1})}} + {{:helper.link("Off", '', {"stopic":"radio", "talk":1}, 'selected')}} + {{/if}} +
+
+ +
+
+ Frequency: +
+
+ {{:helper.link("--", '', {"stopic":"radio", "freq":-10})}} + {{:helper.link("-", '', {"stopic":"radio", "freq": -2})}} +
{{:data.frequency}}
+ {{:helper.link("+", '', {"stopic":"radio", "freq": 2})}} + {{:helper.link("++", '', {"stopic":"radio", "freq": 10})}} +
+
+ +{{for data.channels}} +
+
+ {{:value.name}} +
+
+ {{if value.listening}} + {{:helper.link("On", '', {"stopic":"radio", "channel":value.name, "listen":1}, 'selected')}} + {{:helper.link("Off", '', {"stopic":"radio", "channel":value.name, "listen":1})}} + {{else}} + {{:helper.link("On", '', {"stopic":"radio", "channel":value.name, "listen":1})}} + {{:helper.link("Off", '', {"stopic":"radio", "channel":value.name, "listen":1}, 'selected')}} + {{/if}} +
+
+{{/for}} \ No newline at end of file diff --git a/nano/templates/pai_secrecords.tmpl b/nano/templates/pai_secrecords.tmpl new file mode 100644 index 00000000000..e139034c0de --- /dev/null +++ b/nano/templates/pai_secrecords.tmpl @@ -0,0 +1,76 @@ + + +{{if data.records}} + {{for data.records}} +
+ {{:helper.link(value.name, '', {"stopic":"sec_records", "select":value.ref})}} +
+ {{/for}} +{{/if}} +
+{{if data.general}} +
+
Name
+
{{:data.general.name}}
+
+
+
Record ID
+
{{:data.general.id}}
+
+
+
Sex
+
{{:data.general.sex}}
+
+
+
Species
+
{{:data.general.species}}
+
+
+
Age
+
{{:data.general.age}}
+
+
+
Rank
+
{{:data.general.rank}}
+
+
+
Fingerprint
+
{{:data.general.fingerprint}}
+
+
+
Physical Status
+
{{:data.general.p_stat}}
+
+
+
Mental Status
+
{{:data.general.m_stat}}
+
+{{/if}} +{{if data.security}} +
+
Criminal Status
+
{{:data.security.criminal}}
+
+
+
Minor Crimes
+
{{:data.security.mi_crim}}
+
{{:data.security.mi_crim_d}}
+
+
+
Major Crimes
+
{{:data.security.ma_crim}}
+
{{:data.security.ma_crim_d}}
+
+
+
Important Notes
+
{{:data.security.notes}}
+
+{{/if}} + +{{if data.could_not_find}} +
+ Failed to find some records; the information above may be incomplete or missing. +
+{{/if}} \ No newline at end of file diff --git a/nano/templates/pai_signaller.tmpl b/nano/templates/pai_signaller.tmpl new file mode 100644 index 00000000000..20c2781bc9b --- /dev/null +++ b/nano/templates/pai_signaller.tmpl @@ -0,0 +1,33 @@ + + +
+
+ Frequency: +
+
+ {{:helper.link("--", '', {"stopic":"signaller", "freq":-10})}} + {{:helper.link("-", '', {"stopic":"signaller", "freq": -2})}} +
{{:data.frequency}}
+ {{:helper.link("+", '', {"stopic":"signaller", "freq": 2})}} + {{:helper.link("++", '', {"stopic":"signaller", "freq": 10})}} +
+
+ +
+
+ Code: +
+
+ {{:helper.link("--", '', {"stopic":"signaller", "code":-10})}} + {{:helper.link("-", '', {"stopic":"signaller", "code": -1})}} +
{{:data.code}}
+ {{:helper.link("+", '', {"stopic":"signaller", "code": 1})}} + {{:helper.link("++", '', {"stopic":"signaller", "code": 10})}} +
+
+ +
+ {{:helper.link("Send", '', {"stopic":"signaller", "send":1})}} +
\ No newline at end of file From 70094884ff8ede3c5a57c11df4ad5bc35c2afb0e Mon Sep 17 00:00:00 2001 From: GinjaNinja32 Date: Mon, 9 Feb 2015 10:41:08 +0000 Subject: [PATCH 2/2] Fixes, code cleanup, adds the messenger I forgot last night --- code/modules/mob/living/silicon/pai/pai.dm | 1 + .../living/silicon/pai/software_modules.dm | 82 ++++++++++++++++++- nano/templates/pai_atmosphere.tmpl | 2 +- nano/templates/pai_interface.tmpl | 1 - nano/templates/pai_messenger.tmpl | 80 ++++++++++++++++++ 5 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 nano/templates/pai_messenger.tmpl diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index bcca7b5bdb9..edfe9bea0b6 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -69,6 +69,7 @@ var/translator_on = 0 // keeps track of the translator module + var/current_pda_messaging = null /mob/living/silicon/pai/New(var/obj/item/device/paicard) diff --git a/code/modules/mob/living/silicon/pai/software_modules.dm b/code/modules/mob/living/silicon/pai/software_modules.dm index dcb38e5efdd..c2fed821655 100644 --- a/code/modules/mob/living/silicon/pai/software_modules.dm +++ b/code/modules/mob/living/silicon/pai/software_modules.dm @@ -66,7 +66,7 @@ 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) + v.show_message("[M] presses \his thumb against [P].", 3, "[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) @@ -145,6 +145,76 @@ id = "messenger" toggle = 0 + on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) + var/data[0] + + data["receiver_off"] = user.pda.toff + data["ringer_off"] = user.pda.message_silent + data["current_ref"] = null + data["current_name"] = user.current_pda_messaging + + var/pdas[0] + if(!user.pda.toff) + for(var/obj/item/device/pda/P in sortAtom(PDAs)) + if(!P.owner || P.toff || P == user.pda || P.hidden) continue + var/pda[0] + pda["name"] = "[P]" + pda["owner"] = "[P.owner]" + pda["ref"] = "\ref[P]" + if(P.owner == user.current_pda_messaging) + data["current_ref"] = "\ref[P]" + pdas[++pdas.len] = pda + + data["pdas"] = pdas + + var/messages[0] + if(user.current_pda_messaging) + for(var/index in user.pda.tnote) + if(index["owner"] != user.current_pda_messaging) + continue + var/msg[0] + var/sent = index["sent"] + msg["sent"] = sent ? 1 : 0 + msg["target"] = index["owner"] + msg["message"] = index["message"] + messages[++messages.len] = msg + + data["messages"] = messages + + ui = nanomanager.try_update_ui(user, user, id, ui, data, force_open) + if(!ui) + // Don't copy-paste this unless you're making a pAI software module! + ui = new(user, user, id, "pai_messenger.tmpl", "Digital Messenger", 450, 600) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + + Topic(href, href_list) + var/mob/living/silicon/pai/P = usr + if(!istype(P)) return + + if(!isnull(P.pda)) + if(href_list["toggler"]) + P.pda.toff = href_list["toggler"] != "1" + return 1 + else if(href_list["ringer"]) + P.pda.message_silent = href_list["ringer"] != "1" + return 1 + else if(href_list["select"]) + var/s = href_list["select"] + if(s == "*NONE*") + P.current_pda_messaging = null + else + P.current_pda_messaging = s + return 1 + else if(href_list["target"]) + if(P.silence_time) + return alert("Communications circuits remain uninitialized.") + + var/target = locate(href_list["target"]) + P.pda.create_message(P, target, 1) + return 1 + /datum/pai_software/med_records name = "Medical Records" ram_cost = 15 @@ -221,7 +291,7 @@ var/datum/data/record/S = user.securityActive2 data["general"] = G ? G.fields : null data["security"] = S ? S.fields : null - data["could_not_find"] = user.medical_cannotfind + data["could_not_find"] = user.security_cannotfind ui = nanomanager.try_update_ui(user, user, id, ui, data, force_open) if(!ui) @@ -241,15 +311,19 @@ var/datum/data/record/R = record var/datum/data/record/S = null if (!( data_core.general.Find(R) )) - P.medical_cannotfind = 1 + P.securityActive1 = null + P.securityActive2 = null + P.security_cannotfind = 1 else - P.medical_cannotfind = 0 + P.security_cannotfind = 0 for(var/datum/data/record/E in data_core.security) if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) S = E P.securityActive1 = R P.securityActive2 = S else + P.securityActive1 = null + P.securityActive2 = null P.security_cannotfind = 1 return 1 diff --git a/nano/templates/pai_atmosphere.tmpl b/nano/templates/pai_atmosphere.tmpl index 898bf7bc453..b1a0b0404b8 100644 --- a/nano/templates/pai_atmosphere.tmpl +++ b/nano/templates/pai_atmosphere.tmpl @@ -1,4 +1,4 @@ - diff --git a/nano/templates/pai_interface.tmpl b/nano/templates/pai_interface.tmpl index 27c8a9fcf7f..e6441932665 100644 --- a/nano/templates/pai_interface.tmpl +++ b/nano/templates/pai_interface.tmpl @@ -19,7 +19,6 @@ code/modules/mob/living/silicon/pai/software.dm {{for data.bought}} - {{:helper.link(value.name, '', {"software" : value.id}, null, value.on ? 'selected' : null)}} {{/for}} diff --git a/nano/templates/pai_messenger.tmpl b/nano/templates/pai_messenger.tmpl new file mode 100644 index 00000000000..fb20f826f27 --- /dev/null +++ b/nano/templates/pai_messenger.tmpl @@ -0,0 +1,80 @@ + + +
+
+ Receiver: +
+
+ {{if data.receiver_off}} + {{:helper.link("On", '', {"stopic":"messenger", "toggler":1})}} + {{:helper.link("Off", '', {"stopic":"messenger", "toggler":2}, 'selected')}} + {{else}} + {{:helper.link("On", '', {"stopic":"messenger", "toggler":1}, 'selected')}} + {{:helper.link("Off", '', {"stopic":"messenger", "toggler":2})}} + {{/if}} +
+
+ +
+
+ Ringer: +
+
+ {{if data.ringer_off}} + {{:helper.link("On", '', {"stopic":"messenger", "ringer":1})}} + {{:helper.link("Off", '', {"stopic":"messenger", "ringer":2}, 'selected')}} + {{else}} + {{:helper.link("On", '', {"stopic":"messenger", "ringer":1}, 'selected')}} + {{:helper.link("Off", '', {"stopic":"messenger", "ringer":2})}} + {{/if}} +
+
+ +{{for data.pdas}} +
+ {{:helper.link(value.name, '', {"stopic":"messenger", "select":value.owner})}} + {{:helper.link("Quick Message", '', {"stopic":"messenger", "target":value.ref})}} +
+{{/for}} +
+{{if data.current_ref}} +
+
+ Selected PDA: +
+
+
{{:data.current_name}}
+ {{:helper.link("Send Message", '', {"stopic":"messenger", "target":data.current_ref})}} +
+
+{{else data.current_name}} +
+
+ Selected PDA: +
+
+ {{:data.current_name}} (Cannot send!) +
+
+{{/if}} + +{{for data.messages}} +
+
+ {{if value.sent}} + To {{:value.target}}: + {{else}} + From {{:value.target}}: + {{/if}} +
+
+ {{:value.message}} +
+
+{{/for}} + +{{if data.current_name}} + {{:helper.link("Clear Screen", '', {"stopic":"messenger", "select":"*NONE*"})}} +{{/if}}