diff --git a/code/__defines/pda.dm b/code/__defines/pda.dm new file mode 100644 index 0000000000..c5d32f03ba --- /dev/null +++ b/code/__defines/pda.dm @@ -0,0 +1,3 @@ +#define PDA_APP_UPDATE 0 +#define PDA_APP_NOUPDATE 1 +#define PDA_APP_UPDATE_SLOW 2 diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm index 02a8806089..a9aac1ffcf 100644 --- a/code/_helpers/text.dm +++ b/code/_helpers/text.dm @@ -21,6 +21,27 @@ /* * Text sanitization */ +// Can be used almost the same way as normal input for text +/proc/clean_input(Message, Title, Default, mob/user=usr) + var/txt = input(user, Message, Title, Default) as text | null + if(txt) + return html_encode(txt) + +//Simply removes < and > and limits the length of the message +/proc/strip_html_simple(var/t,var/limit=MAX_MESSAGE_LEN) + var/list/strip_chars = list("<",">") + t = copytext(t,1,limit) + for(var/char in strip_chars) + var/index = findtext(t, char) + while(index) + t = copytext(t, 1, index) + copytext(t, index+1) + index = findtext(t, char) + return t + +//Runs byond's sanitization proc along-side strip_html_simple +//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' that html_encode() would cause +/proc/adminscrub(var/t,var/limit=MAX_MESSAGE_LEN) + return copytext((html_encode(strip_html_simple(t))),1,limit) //Used for preprocessing entered text /proc/sanitize(var/input, var/max_length = MAX_MESSAGE_LEN, var/encode = 1, var/trim = 1, var/extra = 1) diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm index 900f5b9d33..29863f5617 100644 --- a/code/modules/nano/nanoui.dm +++ b/code/modules/nano/nanoui.dm @@ -109,6 +109,7 @@ nanoui is used to open and update nano browser uis add_script("nano_state_manager.js") // The NanoStateManager JS, it handles updates from the server and passes data to the current state add_script("nano_state.js") // The NanoState JS, this is the base state which all states must inherit from add_script("nano_state_default.js") // The NanoStateDefault JS, this is the "default" state (used by all UIs by default), which inherits from NanoState + add_script("nano_state_pda.js") // The NanoStatePDA JS, this is the state used for PDAs. add_script("nano_base_callbacks.js") // The NanoBaseCallbacks JS, this is used to set up (before and after update) callbacks which are common to all UIs add_script("nano_base_helpers.js") // The NanoBaseHelpers JS, this is used to set up template helpers which are common to all UIs add_stylesheet("shared.css") // this CSS sheet is common to all UIs diff --git a/code/modules/pda/app.dm b/code/modules/pda/app.dm index 655bb891de..055e302e10 100644 --- a/code/modules/pda/app.dm +++ b/code/modules/pda/app.dm @@ -5,7 +5,7 @@ var/notify_silent = 0 var/hidden = 0 // program not displayed in main menu var/category = "General" // the category to list it in on the main menu - var/obj/item/pda/pda // if this is null, and the app is running code, something's gone wrong + var/obj/item/device/pda/pda // if this is null, and the app is running code, something's gone wrong /datum/data/pda/Destroy() pda = null @@ -84,7 +84,7 @@ var/base_name category = "Scanners" -/datum/data/pda/utility/scanmode/New(obj/item/cartridge/C) +/datum/data/pda/utility/scanmode/New(obj/item/weapon/cartridge/C) ..(C) name = "Enable [base_name]" diff --git a/code/modules/pda/cart.dm b/code/modules/pda/cart.dm index 5a404288e3..75fdc28c31 100644 --- a/code/modules/pda/cart.dm +++ b/code/modules/pda/cart.dm @@ -80,9 +80,13 @@ var/list/civilian_cartridges = list( var/message1 // used for status_displays var/message2 var/list/stored_data = list() + var/list/programs = list() + var/list/messenger_plugins = list() /obj/item/weapon/cartridge/Destroy() QDEL_NULL(radio) + QDEL_LIST(programs) + QDEL_LIST(messenger_plugins) return ..() /obj/item/weapon/cartridge/engineering @@ -234,14 +238,25 @@ var/list/civilian_cartridges = list( name = "\improper Value-PAK cartridge" desc = "Now with 200% more value!" icon_state = "cart-c" - access_quartermaster = 1 - access_janitor = 1 - access_engine = 1 - access_security = 1 - access_medical = 1 - access_reagent_scanner = 1 - access_status_display = 1 - access_atmos = 1 + programs = list( + new/datum/data/pda/app/power, + new/datum/data/pda/utility/scanmode/halogen, + + new/datum/data/pda/utility/scanmode/gas, + + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical, + + new/datum/data/pda/utility/scanmode/reagent, + + new/datum/data/pda/app/crew_records/security, + new/datum/data/pda/app/secbot_control, + + new/datum/data/pda/app/janitor, + + new/datum/data/pda/app/supply, + + new/datum/data/pda/app/status_display) /obj/item/weapon/cartridge/syndicate name = "\improper Detomatix cartridge" @@ -276,6 +291,13 @@ var/list/civilian_cartridges = list( frequency.post_signal(src, status_signal) +/obj/item/weapon/cartridge/frame + name = "F.R.A.M.E. cartridge" + icon_state = "cart" + charges = 5 + var/telecrystals = 0 + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/frame) + /* This generates the nano values of the cart menus. diff --git a/code/modules/pda/cart_apps.dm b/code/modules/pda/cart_apps.dm new file mode 100644 index 0000000000..0754e7fb92 --- /dev/null +++ b/code/modules/pda/cart_apps.dm @@ -0,0 +1,410 @@ +/datum/data/pda/app/status_display + name = "Status Display" + icon = "list-alt" + template = "pda_status_display" + category = "Utilities" + + var/message1 // used for status_displays + var/message2 + +/datum/data/pda/app/status_display/update_ui(mob/user as mob, list/data) + data["records"] = list( + "message1" = message1 ? message1 : "(none)", + "message2" = message2 ? message2 : "(none)") + +/datum/data/pda/app/status_display/Topic(href, list/href_list) + switch(href_list["choice"]) + if("Status") + switch(href_list["statdisp"]) + if("message") + post_status("message", message1, message2) + if("alert") + post_status("alert", href_list["alert"]) + if("setmsg1") + message1 = clean_input("Line 1", "Enter Message Text", message1) + if("setmsg2") + message2 = clean_input("Line 2", "Enter Message Text", message2) + else + post_status(href_list["statdisp"]) + +/datum/data/pda/app/status_display/proc/post_status(var/command, var/data1, var/data2) + var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435) + if(!frequency) + return + + var/datum/signal/status_signal = new + status_signal.source = src + status_signal.transmission_method = 1 + status_signal.data["command"] = command + + switch(command) + if("message") + status_signal.data["msg1"] = data1 + status_signal.data["msg2"] = data2 + var/mob/user = pda.fingerprintslast + if(istype(pda.loc, /mob/living)) + name = pda.loc + log_admin("STATUS: [user] set status screen with [pda]. Message: [data1] [data2]") + message_admins("STATUS: [user] set status screen with [pda]. Message: [data1] [data2]") + + if("alert") + status_signal.data["picture_state"] = data1 + + spawn(0) + frequency.post_signal(src, status_signal) + + +/datum/data/pda/app/signaller + name = "Signaler System" + icon = "rss" + template = "pda_signaller" + category = "Utilities" + +/datum/data/pda/app/signaller/update_ui(mob/user as mob, list/data) + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/radio/integrated/signal)) + var/obj/item/radio/integrated/signal/R = pda.cartridge.radio + data["signal_freq"] = format_frequency(R.frequency) + data["signal_code"] = R.code + +/datum/data/pda/app/signaller/Topic(href, list/href_list) + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/radio/integrated/signal)) + var/obj/item/radio/integrated/signal/R = pda.cartridge.radio + + switch(href_list["choice"]) + if("Send Signal") + spawn(0) + R.send_signal("ACTIVATE") + + if("Signal Frequency") + var/new_frequency = sanitize_frequency(R.frequency + text2num(href_list["sfreq"])) + R.set_frequency(new_frequency) + + if("Signal Code") + R.code += text2num(href_list["scode"]) + R.code = round(R.code) + R.code = min(100, R.code) + R.code = max(1, R.code) + +/datum/data/pda/app/power + name = "Power Monitor" + icon = "exclamation-triangle" + template = "pda_power" + category = "Engineering" + update = PDA_APP_UPDATE_SLOW + +// var/obj/machinery/computer/monitor/powmonitor = null + +// /datum/data/pda/app/power/update_ui(mob/user as mob, list/data) +// update = PDA_APP_UPDATE_SLOW + +// if(powmonitor && !isnull(powmonitor.powernet)) +// data["records"] = list( +// "powerconnected" = 1, +// "poweravail" = powmonitor.powernet.avail, +// "powerload" = num2text(powmonitor.powernet.viewload, 10), +// "powerdemand" = powmonitor.powernet.load, +// "apcs" = GLOB.apc_repository.apc_data(powmonitor.powernet)) +// has_back = 1 +// else +// data["records"] = list( +// "powerconnected" = 0, +// "powermonitors" = GLOB.powermonitor_repository.powermonitor_data()) +// has_back = 0 + +// /datum/data/pda/app/power/Topic(href, list/href_list) +// switch(href_list["choice"]) +// if("Power Select") +// var/pref = href_list["target"] +// powmonitor = locate(pref) +// update = PDA_APP_UPDATE +// if("Back") +// powmonitor = null +// update = PDA_APP_UPDATE + +/datum/data/pda/app/crew_records + var/datum/data/record/general_records = null + +/datum/data/pda/app/crew_records/update_ui(mob/user as mob, list/data) + var/list/records[0] + + if(general_records && (general_records in data_core.general)) + data["records"] = records + records["general"] = general_records.fields + return records + else + for(var/A in sortRecord(data_core.general)) + var/datum/data/record/R = A + if(R) + records += list(list(Name = R.fields["name"], "ref" = "\ref[R]")) + data["recordsList"] = records + return null + +/datum/data/pda/app/crew_records/Topic(href, list/href_list) + switch(href_list["choice"]) + if("Records") + var/datum/data/record/R = locate(href_list["target"]) + if(R && (R in data_core.general)) + load_records(R) + if("Back") + general_records = null + has_back = 0 + +/datum/data/pda/app/crew_records/proc/load_records(datum/data/record/R) + general_records = R + has_back = 1 + +/datum/data/pda/app/crew_records/medical + name = "Medical Records" + icon = "heartbeat" + template = "pda_medical" + category = "Medical" + + var/datum/data/record/medical_records = null + +/datum/data/pda/app/crew_records/medical/update_ui(mob/user as mob, list/data) + var/list/records = ..() + if(!records) + return + + if(medical_records && (medical_records in data_core.medical)) + records["medical"] = medical_records.fields + + return records + +/datum/data/pda/app/crew_records/medical/load_records(datum/data/record/R) + ..(R) + for(var/A in data_core.medical) + var/datum/data/record/E = A + if(E && (E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) + medical_records = E + break + +/datum/data/pda/app/crew_records/security + name = "Security Records" + icon = "tags" + template = "pda_security" + category = "Security" + + var/datum/data/record/security_records = null + +/datum/data/pda/app/crew_records/security/update_ui(mob/user as mob, list/data) + var/list/records = ..() + if(!records) + return + + if(security_records && (security_records in data_core.security)) + records["security"] = security_records.fields + + return records + +/datum/data/pda/app/crew_records/security/load_records(datum/data/record/R) + ..(R) + for(var/A in data_core.security) + var/datum/data/record/E = A + if(E && (E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) + security_records = E + break + +/datum/data/pda/app/secbot_control + name = "Security Bot Access" + icon = "rss" + template = "pda_secbot" + category = "Security" + +/datum/data/pda/app/secbot_control/update_ui(mob/user as mob, list/data) + var/botsData[0] + var/beepskyData[0] + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/radio/integrated/beepsky)) + var/obj/item/radio/integrated/beepsky/SC = pda.cartridge.radio + beepskyData["active"] = SC.active ? sanitize(SC.active.name) : null + has_back = SC.active ? 1 : 0 + if(SC.active && !isnull(SC.botstatus)) + var/area/loca = SC.botstatus["loca"] + var/loca_name = sanitize(loca.name) + beepskyData["botstatus"] = list("loca" = loca_name, "mode" = SC.botstatus["mode"]) + else + beepskyData["botstatus"] = list("loca" = null, "mode" = -1) + var/botsCount=0 + if(SC.botlist && SC.botlist.len) + for(var/mob/living/bot/B in SC.botlist) + botsCount++ + if(B.loc) + botsData[++botsData.len] = list("Name" = sanitize(B.name), "Location" = sanitize(B.loc.loc.name), "ref" = "\ref[B]") + + if(!botsData.len) + botsData[++botsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "ref"= null) + + beepskyData["bots"] = botsData + beepskyData["count"] = botsCount + + else + beepskyData["active"] = 0 + botsData[++botsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "ref"= null) + beepskyData["botstatus"] = list("loca" = null, "mode" = null) + beepskyData["bots"] = botsData + beepskyData["count"] = 0 + has_back = 0 + + data["beepsky"] = beepskyData + +/datum/data/pda/app/secbot_control/Topic(href, list/href_list) + switch(href_list["choice"]) + if("Back") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/radio/integrated/beepsky)) + pda.cartridge.radio.Topic(null, list(radiomenu = "1", op = "botlist")) + +/datum/data/pda/app/mule_control + name = "Delivery Bot Control" + icon = "truck" + template = "pda_mule" + category = "Quartermaster" + +// /datum/data/pda/app/mule_control/update_ui(mob/user as mob, list/data) +// var/muleData[0] +// var/mulebotsData[0] +// if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/radio/integrated/mule)) +// var/obj/item/radio/integrated/mule/QC = pda.cartridge.radio +// muleData["active"] = QC.active ? sanitize(QC.active.name) : null +// has_back = QC.active ? 1 : 0 +// if(QC.active && !isnull(QC.botstatus)) +// var/area/loca = QC.botstatus["loca"] +// var/loca_name = sanitize(loca.name) +// muleData["botstatus"] = list("loca" = loca_name, "mode" = QC.botstatus["mode"],"home"=QC.botstatus["home"],"powr" = QC.botstatus["powr"],"retn" =QC.botstatus["retn"], "pick"=QC.botstatus["pick"], "load" = QC.botstatus["load"], "dest" = sanitize(QC.botstatus["dest"])) + +// else +// muleData["botstatus"] = list("loca" = null, "mode" = -1,"home"=null,"powr" = null,"retn" =null, "pick"=null, "load" = null, "dest" = null) + + +// var/mulebotsCount=0 +// for(var/mob/living/bot/B in QC.botlist) +// mulebotsCount++ +// if(B.loc) +// mulebotsData[++mulebotsData.len] = list("Name" = sanitize(B.name), "Location" = sanitize(B.loc.loc.name), "ref" = "\ref[B]") + +// if(!mulebotsData.len) +// mulebotsData[++mulebotsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "ref"= null) + +// muleData["bots"] = mulebotsData +// muleData["count"] = mulebotsCount + +// else +// muleData["botstatus"] = list("loca" = null, "mode" = -1,"home"=null,"powr" = null,"retn" =null, "pick"=null, "load" = null, "dest" = null) +// muleData["active"] = 0 +// mulebotsData[++mulebotsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "ref"= null) +// muleData["bots"] = mulebotsData +// muleData["count"] = 0 +// has_back = 0 + +// data["mulebot"] = muleData + +// /datum/data/pda/app/mule_control/Topic(href, list/href_list) +// switch(href_list["choice"]) +// if("Back") +// if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/radio/integrated/mule)) +// pda.cartridge.radio.Topic(null, list(radiomenu = "1", op = "botlist")) + +/datum/data/pda/app/supply + name = "Supply Records" + icon = "file-text-o" + template = "pda_supply" + category = "Quartermaster" + update = PDA_APP_UPDATE_SLOW + +// /datum/data/pda/app/supply/update_ui(mob/user as mob, list/data) +// var/supplyData[0] + +// if(SSshuttle.supply.mode == SHUTTLE_CALL) +// supplyData["shuttle_moving"] = 1 + +// if(is_station_level(SSshuttle.supply.z)) +// supplyData["shuttle_loc"] = "Station" +// else +// supplyData["shuttle_loc"] = "CentCom" + +// supplyData["shuttle_time"] = "([SSshuttle.supply.timeLeft(600)] Mins)" + +// var/supplyOrderCount = 0 +// var/supplyOrderData[0] +// for(var/S in SSshuttle.shoppinglist) +// var/datum/supply_order/SO = S +// supplyOrderCount++ +// supplyOrderData[++supplyOrderData.len] = list("Number" = SO.ordernum, "Name" = html_encode(SO.object.name), "ApprovedBy" = SO.orderedby, "Comment" = html_encode(SO.comment)) + +// if(!supplyOrderData.len) +// supplyOrderData[++supplyOrderData.len] = list("Number" = null, "Name" = null, "OrderedBy"=null) + +// supplyData["approved"] = supplyOrderData +// supplyData["approved_count"] = supplyOrderCount + +// var/requestCount = 0 +// var/requestData[0] +// for(var/S in SSshuttle.requestlist) +// var/datum/supply_order/SO = S +// requestCount++ +// requestData[++requestData.len] = list("Number" = SO.ordernum, "Name" = html_encode(SO.object.name), "OrderedBy" = SO.orderedby, "Comment" = html_encode(SO.comment)) + +// if(!requestData.len) +// requestData[++requestData.len] = list("Number" = null, "Name" = null, "orderedBy" = null, "Comment" = null) + +// supplyData["requests"] = requestData +// supplyData["requests_count"] = requestCount + +// data["supply"] = supplyData + +/datum/data/pda/app/janitor + name = "Custodial Locator" + icon = "trash-o" + template = "pda_janitor" + category = "Utilities" + update = PDA_APP_UPDATE_SLOW + +/datum/data/pda/app/janitor/update_ui(mob/user as mob, list/data) + var/JaniData[0] + var/turf/cl = get_turf(pda) + + if(cl) + JaniData["user_loc"] = list("x" = cl.x, "y" = cl.y) + else + JaniData["user_loc"] = list("x" = 0, "y" = 0) + + var/MopData[0] + for(var/obj/item/weapon/mop/M in all_mops)//GLOB.janitorial_equipment) + var/turf/ml = get_turf(M) + if(ml) + if(ml.z != cl.z) + continue + var/direction = get_dir(pda, M) + MopData[++MopData.len] = list ("x" = ml.x, "y" = ml.y, "dir" = uppertext(dir2text(direction)), "status" = M.reagents.total_volume ? "Wet" : "Dry") + + var/BucketData[0] + for(var/obj/structure/mopbucket/B in all_mopbuckets)//GLOB.janitorial_equipment) + var/turf/bl = get_turf(B) + if(bl) + if(bl.z != cl.z) + continue + var/direction = get_dir(pda,B) + BucketData[++BucketData.len] = list ("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "volume" = B.reagents.total_volume, "max_volume" = B.reagents.maximum_volume) + + var/CbotData[0] + for(var/mob/living/bot/cleanbot/B in mob_list) + var/turf/bl = get_turf(B) + if(bl) + if(bl.z != cl.z) + continue + var/direction = get_dir(pda,B) + CbotData[++CbotData.len] = list("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "status" = B.on ? "Online" : "Offline") + + var/CartData[0] + for(var/obj/structure/janitorialcart/B in all_janitorial_carts)//GLOB.janitorial_equipment) + var/turf/bl = get_turf(B) + if(bl) + if(bl.z != cl.z) + continue + var/direction = get_dir(pda,B) + CartData[++CartData.len] = list("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "volume" = B.reagents.total_volume, "max_volume" = B.reagents.maximum_volume) + + JaniData["mops"] = MopData.len ? MopData : null + JaniData["buckets"] = BucketData.len ? BucketData : null + JaniData["cleanbots"] = CbotData.len ? CbotData : null + JaniData["carts"] = CartData.len ? CartData : null + data["janitor"] = JaniData diff --git a/code/modules/pda/core_apps.dm b/code/modules/pda/core_apps.dm new file mode 100644 index 0000000000..468b9005f5 --- /dev/null +++ b/code/modules/pda/core_apps.dm @@ -0,0 +1,111 @@ +/datum/data/pda/app/main_menu + icon = "home" + template = "pda_main_menu" + hidden = 1 + +/datum/data/pda/app/main_menu/update_ui(mob/user as mob, list/data) + title = pda.name + + data["app"]["is_home"] = 1 + + data["apps"] = pda.shortcut_cache + data["categories"] = pda.shortcut_cat_order + data["pai"] = !isnull(pda.pai) // pAI inserted? + + var/list/notifying[0] + for(var/P in pda.notifying_programs) + notifying["\ref[P]"] = 1 + data["notifying"] = notifying + +/datum/data/pda/app/main_menu/Topic(href, list/href_list) + switch(href_list["choice"]) + if("UpdateInfo") + pda.ownjob = pda.id.assignment + pda.ownrank = pda.id.rank + pda.name = "PDA-[pda.owner] ([pda.ownjob])" + if("pai") + if(pda.pai) + if(pda.pai.loc != pda) + pda.pai = null + else + switch(href_list["option"]) + if("1") // Configure pAI device + pda.pai.attack_self(usr) + if("2") // Eject pAI device + var/turf/T = get_turf_or_move(pda.loc) + if(T) + pda.pai.loc = T + pda.pai = null + +/datum/data/pda/app/notekeeper + name = "Notekeeper" + icon = "sticky-note-o" + template = "pda_notekeeper" + + var/note = null + var/notehtml = "" + +/datum/data/pda/app/notekeeper/start() + . = ..() + if(!note) + note = "Congratulations, your station has chosen the [pda.model_name]!" + +/datum/data/pda/app/notekeeper/update_ui(mob/user as mob, list/data) + data["note"] = note // current pda notes + +/datum/data/pda/app/notekeeper/Topic(href, list/href_list) + switch(href_list["choice"]) + if("Edit") + var/n = input("Please enter message", name, notehtml) as message + if(pda.loc == usr) + note = adminscrub(n) + notehtml = html_decode(note) + note = replacetext(note, "\n", "
") + else + pda.close(usr) + +/datum/data/pda/app/manifest + name = "Crew Manifest" + icon = "user" + template = "pda_manifest" + update = PDA_APP_UPDATE_SLOW + +// /datum/data/pda/app/manifest/update_ui(mob/user as mob, list/data) + // data_core.get_manifest_json() + // data["manifest"] = PDA_Manifest + +/datum/data/pda/app/manifest/Topic(href, list/href_list) + +/datum/data/pda/app/atmos_scanner + name = "Atmospheric Scan" + icon = "fire" + template = "pda_atmos_scan" + category = "Utilities" + update = PDA_APP_UPDATE_SLOW + +// /datum/data/pda/app/atmos_scanner/update_ui(mob/user as mob, list/data) +// var/turf/T = get_turf(user.loc) +// if(!isnull(T)) +// var/datum/gas_mixture/environment = T.return_air() + +// var/pressure = environment.return_pressure() +// var/total_moles = environment.total_moles + +// 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) +// data["aircontents"] = list( +// "pressure" = pressure, +// "nitrogen" = n2_level*100, +// "oxygen" = o2_level*100, +// "carbon_dioxide" = co2_level*100, +// "plasma" = plasma_level*100, +// "other" = unknown_level, +// "temp" = environment.temperature-T0C, +// "reading" = 1 +// ) +// if(isnull(data["aircontents"])) +// data["aircontents"] = list("reading" = 0) diff --git a/code/modules/pda/messenger.dm b/code/modules/pda/messenger.dm new file mode 100644 index 0000000000..578ab9fcfb --- /dev/null +++ b/code/modules/pda/messenger.dm @@ -0,0 +1,215 @@ +/datum/data/pda/app/messenger + name = "Messenger" + icon = "comments-o" + notify_icon = "comments" + title = "SpaceMessenger V4.1.0" + template = "pda_messenger" + + var/toff = 0 //If 1, messenger disabled + var/list/tnote[0] //Current Texts + var/last_text //No text spamming + + var/m_hidden = 0 // Is the PDA hidden from the PDA list? + var/active_conversation = null // New variable that allows us to only view a single conversation. + var/list/conversations = list() // For keeping up with who we have PDA messsages from. + var/latest_post = 0 + var/auto_scroll = 1 + +/datum/data/pda/app/messenger/start() + . = ..() + unnotify() + latest_post = 0 + +/datum/data/pda/app/messenger/update_ui(mob/user as mob, list/data) + data["silent"] = notify_silent // does the pda make noise when it receives a message? + data["toff"] = toff // is the messenger function turned off? + data["active_conversation"] = active_conversation // Which conversation are we following right now? + + has_back = active_conversation + if(active_conversation) + data["messages"] = tnote + for(var/c in tnote) + if(c["target"] == active_conversation) + data["convo_name"] = sanitize(c["owner"]) + data["convo_job"] = sanitize(c["job"]) + break + data["auto_scroll"] = auto_scroll + data["latest_post"] = latest_post + latest_post = tnote.len + else + var/convopdas[0] + var/pdas[0] + for(var/A in PDAs) + var/obj/item/device/pda/P = A + var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) + + if(!P.owner || PM.toff || P == pda || PM.m_hidden) + continue + if(conversations.Find("\ref[P]")) + convopdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "1"))) + else + pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0"))) + + data["convopdas"] = convopdas + data["pdas"] = pdas + + var/list/plugins = list() + if(pda.cartridge) + for(var/A in pda.cartridge.messenger_plugins) + var/datum/data/pda/messenger_plugin/P = A + plugins += list(list(name = P.name, icon = P.icon, ref = "\ref[P]")) + data["plugins"] = plugins + + if(pda.cartridge) + data["charges"] = pda.cartridge.charges ? pda.cartridge.charges : 0 + +/datum/data/pda/app/messenger/Topic(href, list/href_list) + if(!pda.can_use()) + return + unnotify() + + switch(href_list["choice"]) + if("Toggle Messenger") + toff = !toff + if("Toggle Ringer")//If viewing texts then erase them, if not then toggle silent status + notify_silent = !notify_silent + if("Clear")//Clears messages + if(href_list["option"] == "All") + tnote.Cut() + conversations.Cut() + if(href_list["option"] == "Convo") + var/new_tnote[0] + for(var/i in tnote) + if(i["target"] != active_conversation) + new_tnote[++new_tnote.len] = i + tnote = new_tnote + conversations.Remove(active_conversation) + + active_conversation = null + latest_post = 0 + if("Message") + var/obj/item/device/pda/P = locate(href_list["target"]) + create_message(usr, P) + if(href_list["target"] in conversations) // Need to make sure the message went through, if not welp. + active_conversation = href_list["target"] + latest_post = 0 + if("Select Conversation") + var/P = href_list["convo"] + for(var/n in conversations) + if(P == n) + active_conversation = P + latest_post = 0 + if("Messenger Plugin") + if(!href_list["target"] || !href_list["plugin"]) + return + + var/obj/item/device/pda/P = locate(href_list["target"]) + if(!P) + to_chat(usr, "PDA not found.") + + var/datum/data/pda/messenger_plugin/plugin = locate(href_list["plugin"]) + if(plugin && (plugin in pda.cartridge.messenger_plugins)) + plugin.messenger = src + plugin.user_act(usr, P) + if("Back") + active_conversation = null + latest_post = 0 + if("Autoscroll") + auto_scroll = !auto_scroll + +/datum/data/pda/app/messenger/proc/create_message(var/mob/living/U, var/obj/item/device/pda/P) + var/t = input(U, "Please enter message", name, null) as text|null + if(!t) + return + t = sanitize(copytext(t, 1, MAX_MESSAGE_LEN)) + t = readd_quotes(t) + if(!t || !istype(P)) + return + if(!in_range(pda, U) && pda.loc != U) + return + + var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) + + if(!PM || PM.toff || toff) + return + + if(last_text && world.time < last_text + 5) + return + + if(!pda.can_use()) + return + + last_text = world.time + // check if telecomms I/O route 1459 is stable + //var/telecomms_intact = telecomms_process(P.owner, owner, t) + var/obj/machinery/message_server/useMS = null + if(message_servers) + for(var/A in message_servers) + var/obj/machinery/message_server/MS = A + //PDAs are now dependent on the Message Server. + if(MS.active) + useMS = MS + break + + var/datum/signal/signal = pda.telecomms_process() + + var/useTC = 0 + if(signal) + if(signal.data["done"]) + useTC = 1 + var/turf/pos = get_turf(P) + // TODO: Make the radio system cooperate with the space manager + if(pos.z in signal.data["level"]) + useTC = 2 + //Let's make this barely readable + if(signal.data["compression"] > 0) + t = Gibberish(t, signal.data["compression"] + 50) + + if(useMS && useTC) // only send the message if it's stable + if(useTC != 2) // Does our recipient have a broadcaster on their level? + to_chat(U, "ERROR: Cannot reach recipient.") + return + useMS.send_pda_message("[P.owner]","[pda.owner]","[t]") + tnote.Add(list(list("sent" = 1, "owner" = "[P.owner]", "job" = "[P.ownjob]", "message" = "[t]", "target" = "\ref[P]"))) + PM.tnote.Add(list(list("sent" = 0, "owner" = "[pda.owner]", "job" = "[pda.ownjob]", "message" = "[t]", "target" = "\ref[pda]"))) + pda.investigate_log("PDA Message - [U.key] - [pda.owner] -> [P.owner]: [t]", "pda") + if(!conversations.Find("\ref[P]")) + conversations.Add("\ref[P]") + if(!PM.conversations.Find("\ref[pda]")) + PM.conversations.Add("\ref[pda]") + + SSnanoui.update_user_uis(U, P) // Update the sending user's PDA UI so that they can see the new message + PM.notify("Message from [pda.owner] ([pda.ownjob]), \"[t]\" (Reply)") + log_pda("(PDA: [src.name]) sent \"[t]\" to [P.name]", usr) + else + to_chat(U, "ERROR: Messaging server is not responding.") + +/datum/data/pda/app/messenger/proc/available_pdas() + var/list/names = list() + var/list/plist = list() + var/list/namecounts = list() + + if(toff) + to_chat(usr, "Turn on your receiver in order to send messages.") + return + + for(var/A in PDAs) + var/obj/item/device/pda/P = A + var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) + + if(!P.owner || !PM || PM.hidden || P == pda || PM.toff) + continue + + var/name = P.owner + if(name in names) + namecounts[name]++ + name = text("[name] ([namecounts[name]])") + else + names.Add(name) + namecounts[name] = 1 + + plist[text("[name]")] = P + return plist + +/datum/data/pda/app/messenger/proc/can_receive() + return pda.owner && !toff && !hidden diff --git a/code/modules/pda/messenger_plugins.dm b/code/modules/pda/messenger_plugins.dm new file mode 100644 index 0000000000..90cb9460a4 --- /dev/null +++ b/code/modules/pda/messenger_plugins.dm @@ -0,0 +1,91 @@ +/datum/data/pda/messenger_plugin + var/datum/data/pda/app/messenger/messenger + +/datum/data/pda/messenger_plugin/proc/user_act(mob/user as mob, obj/item/device/pda/P) + + +/datum/data/pda/messenger_plugin/virus + name = "*Send Virus*" + +/datum/data/pda/messenger_plugin/virus/user_act(mob/user as mob, obj/item/device/pda/P) + var/datum/data/pda/app/messenger/M = P.find_program(/datum/data/pda/app/messenger) + + if(M && !M.toff && pda.cartridge.charges > 0) + pda.cartridge.charges-- + return 1 + return 0 + + +/datum/data/pda/messenger_plugin/virus/clown + icon = "star" + +/datum/data/pda/messenger_plugin/virus/clown/user_act(mob/user as mob, obj/item/device/pda/P) + . = ..(user, P) + if(.) + user.show_message("Virus sent!", 1) + P.honkamt = (rand(15,20)) + P.ttone = "honk" + + +/datum/data/pda/messenger_plugin/virus/mime + icon = "arrow-circle-down" + +/datum/data/pda/messenger_plugin/virus/mime/user_act(mob/user as mob, obj/item/device/pda/P) + . = ..(user, P) + if(.) + user.show_message("Virus sent!", 1) + var/datum/data/pda/app/M = P.find_program(/datum/data/pda/app/messenger) + if(M) + M.notify_silent = 1 + P.ttone = "silence" + + +/datum/data/pda/messenger_plugin/virus/detonate + name = "*Detonate*" + icon = "exclamation-circle" + +/datum/data/pda/messenger_plugin/virus/detonate/user_act(mob/user as mob, obj/item/device/pda/P) + . = ..(user, P) + if(.) + var/difficulty = 0 + + if(pda.cartridge) + difficulty += pda.cartridge.programs.len / 2 + else + difficulty += 2 + + if(!P.detonate || P.hidden_uplink) + user.show_message("The target PDA does not seem to respond to the detonation command.", 1) + pda.cartridge.charges++ + else if(prob(difficulty * 12)) + user.show_message("An error flashes on your [pda].", 1) + else if(prob(difficulty * 3)) + user.show_message("Energy feeds back into your [pda]!", 1) + pda.close(user) + pda.explode() + log_admin("[key_name(user)] just attempted to blow up [P] with the Detomatix cartridge but failed, blowing themselves up") + message_admins("[key_name_admin(user)] just attempted to blow up [P] with the Detomatix cartridge but failed, blowing themselves up", 1) + else + user.show_message("Success!", 1) + log_admin("[key_name(user)] just attempted to blow up [P] with the Detomatix cartridge and succeded") + message_admins("[key_name_admin(user)] just attempted to blow up [P] with the Detomatix cartridge and succeded", 1) + P.explode() + +/datum/data/pda/messenger_plugin/virus/frame + icon = "exclamation-circle" + +/datum/data/pda/messenger_plugin/virus/frame/user_act(mob/user, obj/item/device/pda/P) + . = ..(user, P) + if(.) + var/lock_code = "[rand(100,999)] [pick("Alpha","Bravo","Charlie","Delta","Echo","Foxtrot","Golf","Hotel","India","Juliet","Kilo","Lima","Mike","November","Oscar","Papa","Quebec","Romeo","Sierra","Tango","Uniform","Victor","Whiskey","X-ray","Yankee","Zulu")]" + user.show_message("Virus Sent! The unlock code to the target is: [lock_code]") + if(!P.hidden_uplink) + var/obj/item/device/uplink/hidden/uplink = new(P) + P.hidden_uplink = uplink + P.lock_code = lock_code + // else + // P.hidden_uplink.hidden_crystals += P.hidden_uplink.uses //Temporarially hide the PDA's crystals, so you can't steal telecrystals. + var/obj/item/weapon/cartridge/frame/parent_cart = pda.cartridge + P.hidden_uplink.uses = parent_cart.telecrystals + parent_cart.telecrystals = 0 + P.hidden_uplink.active = TRUE diff --git a/code/modules/pda/pda.dm b/code/modules/pda/pda.dm index 91784b5de6..bbb27f9a78 100644 --- a/code/modules/pda/pda.dm +++ b/code/modules/pda/pda.dm @@ -18,14 +18,10 @@ var/global/list/obj/item/device/pda/PDAs = list() var/owner = null var/default_cartridge = 0 // Access level defined by cartridge var/obj/item/weapon/cartridge/cartridge = null //current cartridge - var/mode = 0 //Controls what menu the PDA will display. 0 is hub; the rest are either built in or based on cartridge. - - var/lastmode = 0 var/ui_tick = 0 - var/nanoUI[0] //Secondary variables - var/scanmode = 0 //1 is medical scanner, 2 is forensics, 3 is reagent scanner. + // var/scanmode = 0 //1 is medical scanner, 2 is forensics, 3 is reagent scanner. var/fon = 0 //Is the flashlight function on? var/f_lum = 2 //Luminosity for the flashlight function var/message_silent = 0 //To beep or not to beep, that is the question @@ -34,7 +30,14 @@ var/global/list/obj/item/device/pda/PDAs = list() var/tnote[0] //Current Texts var/last_text //No text spamming var/last_honk //Also no honk spamming that's bad too - var/ttone = "beep" //The PDA ringtone! + var/ttone = "beep" //The ringtone! + var/list/ttone_sound = list("beep" = 'sound/machines/twobeep.ogg', + "boom" = 'sound/effects/explosionfar.ogg', + "slip" = 'sound/misc/slip.ogg', + "honk" = 'sound/items/bikehorn.ogg', + "SKREE" = 'sound/voice/shriek1.ogg', + // "holy" = 'sound/items/PDA/ambicha4-short.ogg', + "xeno" = 'sound/voice/hiss1.ogg') var/newstone = "beep, beep" //The news ringtone! var/lock_code = "" // Lockcode to unlock uplink var/honkamt = 0 //How many honks left when infected with honk.exe @@ -67,6 +70,24 @@ var/global/list/obj/item/device/pda/PDAs = list() var/spam_proof = FALSE // If true, it can't be spammed by random events. + // App-based PDAs + var/model_name = "Thinktronic 5230 Personal Data Assistant" + var/datum/data/pda/utility/scanmode/scanmode = null + var/datum/data/pda/app/current_app = null + var/datum/data/pda/app/lastapp = null + var/list/programs = list( + new/datum/data/pda/app/main_menu, + new/datum/data/pda/app/notekeeper, + new/datum/data/pda/app/messenger, + new/datum/data/pda/app/manifest, + new/datum/data/pda/app/atmos_scanner, + new/datum/data/pda/utility/scanmode/notes, + new/datum/data/pda/utility/flashlight) + var/list/shortcut_cache = list() + var/list/shortcut_cat_order = list() + var/list/notifying_programs = list() + var/retro_mode = 0 + /obj/item/device/pda/examine(mob/user) . = ..() if(Adjacent(user)) @@ -97,6 +118,32 @@ var/global/list/obj/item/device/pda/PDAs = list() if(iscarbon(user) && !touch_silent) playsound(src, 'sound/machines/pda_click.ogg', 20) +/obj/item/device/pda/proc/play_ringtone() + var/S + + if(ttone in ttone_sound) + S = ttone_sound[ttone] + else + S = 'sound/machines/twobeep.ogg' + playsound(loc, S, 50, 1) + for(var/mob/O in hearers(3, loc)) + O.show_message(text("[bicon(src)] *[ttone]*")) + +/obj/item/device/pda/proc/set_ringtone() + var/t = input("Please enter new ringtone", name, ttone) as text + if(in_range(src, usr) && loc == usr) + if(t) + if(hidden_uplink && hidden_uplink.check_trigger(usr, lowertext(t), lowertext(lock_code))) + to_chat(usr, "The PDA softly beeps.") + close(usr) + else + t = sanitize(copytext(t, 1, 20)) + ttone = t + return 1 + else + close(usr) + return 0 + /obj/item/device/pda/medical default_cartridge = /obj/item/weapon/cartridge/medical icon_state = "pda-m" @@ -442,6 +489,7 @@ var/global/list/obj/item/device/pda/PDAs = list() ..() PDAs += src PDAs = sortAtom(PDAs) + update_programs() if(default_cartridge) cartridge = new default_cartridge(src) new /obj/item/weapon/pen(src) @@ -469,6 +517,7 @@ var/global/list/obj/item/device/pda/PDAs = list() else icon = 'icons/obj/pda_old.dmi' log_debug("Invalid switch for PDA, defaulting to old PDA icons. [pdachoice] chosen.") + start_program(find_program(/datum/data/pda/app/main_menu)) /obj/item/device/pda/proc/can_use() @@ -499,17 +548,25 @@ var/global/list/obj/item/device/pda/PDAs = list() return attack_self(M) return +/obj/item/device/pda/proc/close(mob/user) + var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") + ui.close() + + SStgui.close_uis(src) /obj/item/device/pda/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) ui_tick++ var/datum/nanoui/old_ui = SSnanoui.get_open_ui(user, src, "main") var/auto_update = 1 - if(mode in no_auto_update) - auto_update = 0 - if(old_ui && (mode == lastmode && ui_tick % 5 && mode in update_every_five)) + if(!current_app) return - lastmode = mode + if(current_app.update == PDA_APP_NOUPDATE && current_app == lastapp) + auto_update = 0 + if(old_ui && (current_app == lastapp && ui_tick % 5 && current_app.update == PDA_APP_UPDATE_SLOW)) + return + + lastapp = current_app var/title = "Personal Data Assistant" @@ -518,156 +575,57 @@ var/global/list/obj/item/device/pda/PDAs = list() data["owner"] = owner // Who is your daddy... data["ownjob"] = ownjob // ...and what does he do? - data["mode"] = mode // The current view - data["scanmode"] = scanmode // Scanners - data["fon"] = fon // Flashlight on? - data["pai"] = (isnull(pai) ? 0 : 1) // pAI inserted? - data["note"] = note // current pda notes - data["message_silent"] = message_silent // does the pda make noise when it receives a message? - data["news_silent"] = news_silent // does the pda make noise when it receives news? - data["touch_silent"] = touch_silent // does the pda make noise when it receives news? - data["toff"] = toff // is the messenger function turned off? - data["active_conversation"] = active_conversation // Which conversation are we following right now? + // update list of shortcuts, only if they changed + if(!shortcut_cache.len) + shortcut_cache = list() + shortcut_cat_order = list() + var/prog_list = programs.Copy() + if(cartridge) + prog_list |= cartridge.programs + for(var/A in prog_list) + var/datum/data/pda/P = A + + if(P.hidden) + continue + var/list/cat + if(P.category in shortcut_cache) + cat = shortcut_cache[P.category] + else + cat = list() + shortcut_cache[P.category] = cat + shortcut_cat_order += P.category + cat |= list(list(name = P.name, icon = P.icon, notify_icon = P.notify_icon, ref = "\ref[P]")) + + // force the order of a few core categories + shortcut_cat_order = list("General") \ + + sortList(shortcut_cat_order - list("General", "Scanners", "Utilities")) \ + + list("Scanners", "Utilities") data["idInserted"] = (id ? 1 : 0) data["idLink"] = (id ? text("[id.registered_name], [id.assignment]") : "--------") - data["cart_loaded"] = cartridge ? 1:0 - if(cartridge) - var/cartdata[0] - cartdata["access"] = list(\ - "access_security" = cartridge.access_security,\ - "access_engine" = cartridge.access_engine,\ - "access_atmos" = cartridge.access_atmos,\ - "access_medical" = cartridge.access_medical,\ - "access_clown" = cartridge.access_clown,\ - "access_mime" = cartridge.access_mime,\ - "access_janitor" = cartridge.access_janitor,\ - "access_quartermaster" = cartridge.access_quartermaster,\ - "access_hydroponics" = cartridge.access_hydroponics,\ - "access_reagent_scanner" = cartridge.access_reagent_scanner,\ - "access_remote_door" = cartridge.access_remote_door,\ - "access_status_display" = cartridge.access_status_display,\ - "access_detonate_pda" = cartridge.access_detonate_pda\ - ) + data["useRetro"] = retro_mode - if(mode in cartmodes) - data["records"] = cartridge.create_NanoUI_values() + data["cartridge_name"] = cartridge ? cartridge.name : "" + data["stationTime"] = worldtime2stationtime(world.time) - if(mode == 0) - cartdata["name"] = cartridge.name - if(isnull(cartridge.radio)) - cartdata["radio"] = 0 - else - if(istype(cartridge.radio, /obj/item/radio/integrated/beepsky)) - cartdata["radio"] = 1 - if(istype(cartridge.radio, /obj/item/radio/integrated/signal)) - cartdata["radio"] = 2 - //if(istype(cartridge.radio, /obj/item/radio/integrated/mule)) - // cartdata["radio"] = 3 + data["app"] = list() + current_app.update_ui(user, data) + data["app"] |= list( + "name" = current_app.title, + "icon" = current_app.icon, + "template" = current_app.template, + "has_back" = current_app.has_back) - if(mode == 2) - cartdata["charges"] = cartridge.charges ? cartridge.charges : 0 - data["cartridge"] = cartdata - - data["stationTime"] = stationtime2text() - data["new_Message"] = new_message - data["new_News"] = new_news - - var/datum/reception/reception = get_reception(src, do_sleep = 0) - var/has_reception = reception.telecomms_reception & TELECOMMS_RECEPTION_SENDER - data["reception"] = has_reception - - if(mode==2) - var/convopdas[0] - var/pdas[0] - var/count = 0 - for (var/obj/item/device/pda/P in PDAs) - if (!P.owner||P.toff||P == src||P.hidden) continue - if(conversations.Find("\ref[P]")) - convopdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "1"))) - else - pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0"))) - count++ - - data["convopdas"] = convopdas - data["pdas"] = pdas - data["pda_count"] = count - - if(mode==21) - data["messagescount"] = tnote.len - data["messages"] = tnote - else - data["messagescount"] = null - data["messages"] = null - - if(active_conversation) - for(var/c in tnote) - if(c["target"] == active_conversation) - data["convo_name"] = sanitize(c["owner"]) - data["convo_job"] = sanitize(c["job"]) - break - if(mode==41) - data_core.get_manifest_list() - - - if(mode==3) - data["aircontents"] = src.analyze_air() - if(mode==6) - if(has_reception) - feeds.Cut() - for(var/datum/feed_channel/channel in news_network.network_channels) - feeds[++feeds.len] = list("name" = channel.channel_name, "censored" = channel.censored) - data["feedChannels"] = feeds - if(mode==61) - var/datum/feed_channel/FC - for(FC in news_network.network_channels) - if(FC.channel_name == active_feed["name"]) - break - - var/list/feed = feed_info[active_feed] - if(!feed) - feed = list() - feed["channel"] = FC.channel_name - feed["author"] = "Unknown" - feed["censored"]= 0 - feed["updated"] = -1 - feed_info[active_feed] = feed - - if(FC.updated > feed["updated"] && has_reception) - feed["author"] = FC.author - feed["updated"] = FC.updated - feed["censored"] = FC.censored - - var/list/messages = list() - if(!FC.censored) - var/index = 0 - for(var/datum/feed_message/FM in FC.messages) - index++ - if(FM.img) - usr << browse_rsc(FM.img, "pda_news_tmp_photo_[feed["channel"]]_[index].png") - // News stories are HTML-stripped but require newline replacement to be properly displayed in NanoUI - var/body = replacetext(FM.body, "\n", "
") - messages[++messages.len] = list("author" = FM.author, "body" = body, "message_type" = FM.message_type, "time_stamp" = FM.time_stamp, "has_image" = (FM.img != null), "caption" = FM.caption, "index" = index) - feed["messages"] = messages - - data["feed"] = feed - - data["manifest"] = PDA_Manifest - - nanoUI = data // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) + if(!ui) // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm ui = new(user, src, ui_key, "pda.tmpl", title, 520, 400, state = inventory_state) - // add templates for screens in common with communicator. - ui.add_template("atmosphericScan", "atmospheric_scan.tmpl") - ui.add_template("crewManifest", "crew_manifest.tmpl") + ui.set_state_key("pda") // when the ui is first opened this is the data it will use ui.set_initial_data(data) // open the new ui window @@ -682,24 +640,42 @@ var/global/list/obj/item/device/pda/PDAs = list() return ui_interact(user) //NanoUI requires this proc + tgui_interact(user) return +/obj/item/device/pda/proc/start_program(datum/data/pda/P) + if(P && ((P in programs) || (cartridge && (P in cartridge.programs)))) + return P.start() + return 0 + +/obj/item/device/pda/proc/find_program(type) + var/datum/data/pda/A = locate(type) in programs + if(A) + return A + if(cartridge) + A = locate(type) in cartridge.programs + if(A) + return A + return null + +// force the cache to rebuild on update_ui +/obj/item/device/pda/proc/update_shortcuts() + shortcut_cache.Cut() + +/obj/item/device/pda/proc/update_programs() + for(var/A in programs) + var/datum/data/pda/P = A + P.pda = src + /obj/item/device/pda/Topic(href, href_list) - if(href_list["cartmenu"] && !isnull(cartridge)) - cartridge.Topic(href, href_list) - return 1 - if(href_list["radiomenu"] && !isnull(cartridge) && !isnull(cartridge.radio)) - cartridge.radio.Topic(href, href_list) - return 1 + . = ..() + if(.) + return - - ..() var/mob/user = usr var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") var/mob/living/U = usr - //Looking for master was kind of pointless since PDAs don't appear to have one. - //if ((src in U.contents) || ( istype(loc, /turf) && in_range(src, U) ) ) - if (usr.stat == DEAD) + if(usr.stat == DEAD) return 0 if(!can_use()) //Why reinvent the wheel? There's a proc that does exactly that. U.unset_machine() @@ -710,293 +686,56 @@ var/global/list/obj/item/device/pda/PDAs = list() add_fingerprint(U) U.set_machine(src) + if(href_list["radiomenu"] && !isnull(cartridge) && !isnull(cartridge.radio)) + cartridge.radio.Topic(href, href_list) + return 1 + + . = 1 + switch(href_list["choice"]) - -//BASIC FUNCTIONS=================================== - - if("Close")//Self explanatory - U.unset_machine() - ui.close() - return 0 - if("Refresh")//Refresh, goes to the end of the proc. - if("Return")//Return - if(mode<=9) - mode = 0 - else - mode = round(mode/10) - if(mode==2) - active_conversation = null - if(mode==4)//Fix for cartridges. Redirects to hub. - mode = 0 - else if(mode >= 40 && mode <= 49)//Fix for cartridges. Redirects to refresh the menu. - cartridge.mode = mode - if ("Authenticate")//Checks for ID - id_check(U, 1) - if("UpdateInfo") - ownjob = id.assignment - ownrank = id.rank - name = "PDA-[owner] ([ownjob])" + if("Home")//Go home, largely replaces the old Return + var/datum/data/pda/app/main_menu/A = find_program(/datum/data/pda/app/main_menu) + if(A) + start_program(A) + if("StartProgram") + if(href_list["program"]) + var/datum/data/pda/app/A = locate(href_list["program"]) + if(A) + start_program(A) if("Eject")//Ejects the cart, only done from hub. - verb_remove_cartridge() - -//MENU FUNCTIONS=================================== - - if("0")//Hub - mode = 0 - if("1")//Notes - mode = 1 - if("2")//Messenger - mode = 2 - if("21")//Read messages - mode = 21 - if("3")//Atmos scan - mode = 3 - if("4")//Redirects to hub - mode = 0 - if("chatroom") // chatroom hub - mode = 5 - if("41") //Manifest - mode = 41 - - -//MAIN FUNCTIONS=================================== - - if("Light") - if(fon) - fon = 0 - set_light(0) - else - fon = 1 - set_light(f_lum) - if("Medical Scan") - if(scanmode == 1) - scanmode = 0 - else if((!isnull(cartridge)) && (cartridge.access_medical)) - scanmode = 1 - if("Reagent Scan") - if(scanmode == 3) - scanmode = 0 - else if((!isnull(cartridge)) && (cartridge.access_reagent_scanner)) - scanmode = 3 - if("Halogen Counter") - if(scanmode == 4) - scanmode = 0 - else if((!isnull(cartridge)) && (cartridge.access_engine)) - scanmode = 4 - if("Honk") - if ( !(last_honk && world.time < last_honk + 20) ) - playsound(src, 'sound/items/bikehorn.ogg', 50, 1) - last_honk = world.time - if("Gas Scan") - if(scanmode == 5) - scanmode = 0 - else if((!isnull(cartridge)) && (cartridge.access_atmos)) - scanmode = 5 - if("Toggle Beeping") - touch_silent = !touch_silent - -//MESSENGER/NOTE FUNCTIONS=================================== - - if ("Edit") - var/n = input(U, "Please enter message", name, notehtml) as message - if (in_range(src, U) && loc == U) - n = sanitizeSafe(n, extra = 0) - if (mode == 1) - note = html_decode(n) - notehtml = note - note = replacetext(note, "\n", "
") - else - ui.close() - if("Toggle Messenger") - toff = !toff - if("Toggle Ringer")//If viewing texts then erase them, if not then toggle silent status - message_silent = !message_silent - if("Toggle News") - news_silent = !news_silent - if("Clear")//Clears messages - if(href_list["option"] == "All") - tnote.Cut() - conversations.Cut() - if(href_list["option"] == "Convo") - var/new_tnote[0] - for(var/i in tnote) - if(i["target"] != active_conversation) - new_tnote[++new_tnote.len] = i - tnote = new_tnote - conversations.Remove(active_conversation) - - active_conversation = null - if(mode==21) - mode=2 - + if(!isnull(cartridge)) + var/turf/T = loc + if(ismob(T)) + T = T.loc + var/obj/item/weapon/cartridge/C = cartridge + C.forceMove(T) + if(scanmode in C.programs) + scanmode = null + if(current_app in C.programs) + start_program(find_program(/datum/data/pda/app/main_menu)) + if(C.radio) + C.radio.hostpda = null + for(var/datum/data/pda/P in notifying_programs) + if(P in C.programs) + P.unnotify() + cartridge = null + update_shortcuts() + if("Authenticate")//Checks for ID + id_check(usr, 1) + if("Retro") + retro_mode = !retro_mode if("Ringtone") - var/t = input(U, "Please enter new ringtone", name, ttone) as text - if (in_range(src, U) && loc == U) - if (t) - if(src.hidden_uplink && hidden_uplink.check_trigger(U, lowertext(t), lowertext(lock_code))) - to_chat(U, "The PDA softly beeps.") - ui.close() - else - t = sanitize(t, 20) - ttone = t - else - ui.close() - return 0 - if("Newstone") - var/t = input(U, "Please enter new news tone", name, newstone) as text - if (in_range(src, U) && loc == U) - if (t) - t = sanitize(t, 20) - newstone = t - else - ui.close() - return 0 - if("Message") - - var/obj/item/device/pda/P = locate(href_list["target"]) - src.create_message(U, P, !href_list["notap"]) - if(mode == 2) - if(href_list["target"] in conversations) // Need to make sure the message went through, if not welp. - active_conversation = href_list["target"] - mode = 21 - - if("Select Conversation") - var/P = href_list["convo"] - for(var/n in conversations) - if(P == n) - active_conversation=P - mode=21 - if("Select Feed") - var/n = href_list["name"] - for(var/f in feeds) - if(f["name"] == n) - active_feed = f - mode=61 - if("Send Honk")//Honk virus - if(cartridge && cartridge.access_clown)//Cartridge checks are kind of unnecessary since everything is done through switch. - var/obj/item/device/pda/P = locate(href_list["target"])//Leaving it alone in case it may do something useful, I guess. - if(!isnull(P)) - if (!P.toff && cartridge.charges > 0) - cartridge.charges-- - U.show_message("Virus sent!", 1) - P.honkamt = (rand(15,20)) - else - to_chat(U, "PDA not found.") - else - ui.close() - return 0 - if("Send Silence")//Silent virus - if(cartridge && cartridge.access_mime) - var/obj/item/device/pda/P = locate(href_list["target"]) - if(!isnull(P)) - if (!P.toff && cartridge.charges > 0) - cartridge.charges-- - U.show_message("Virus sent!", 1) - P.message_silent = 1 - P.news_silent = 1 - P.ttone = "silence" - P.newstone = "silence" - else - to_chat(U, "PDA not found.") - else - ui.close() - return 0 - - -//SYNDICATE FUNCTIONS=================================== - - if("Toggle Door") - if(cartridge && cartridge.access_remote_door) - for(var/obj/machinery/door/blast/M in machines) - if(M.id == cartridge.remote_door_id) - if(M.density) - M.open() - else - M.close() - - if("Detonate")//Detonate PDA... maybe - if(cartridge && cartridge.access_detonate_pda) - var/obj/item/device/pda/P = locate(href_list["target"]) - var/datum/reception/reception = get_reception(src, P, "", do_sleep = 0) - if(!(reception.message_server && reception.telecomms_reception & TELECOMMS_RECEPTION_SENDER)) - U.show_message("An error flashes on your [src]: Connection unavailable", 1) - return - if(reception.telecomms_reception & TELECOMMS_RECEPTION_RECEIVER == 0) // Does our recepient have a broadcaster on their level? - U.show_message("An error flashes on your [src]: Recipient unavailable", 1) - return - if(!isnull(P)) - if (!P.toff && cartridge.charges > 0) - cartridge.charges-- - - var/difficulty = 2 - - if(P.cartridge) - difficulty += P.cartridge.access_medical - difficulty += P.cartridge.access_security - difficulty += P.cartridge.access_engine - difficulty += P.cartridge.access_clown - difficulty += P.cartridge.access_janitor - if(P.hidden_uplink) - difficulty += 3 - - if(prob(difficulty)) - U.show_message("An error flashes on your [src].", 1) - else if (prob(difficulty * 7)) - U.show_message("Energy feeds back into your [src]!", 1) - ui.close() - detonate_act(src) - log_admin("[key_name(U)] just attempted to blow up [P] with the Detomatix cartridge but failed, blowing themselves up") - message_admins("[key_name_admin(U)] just attempted to blow up [P] with the Detomatix cartridge but failed.", 1) - else - U.show_message("Success!", 1) - log_admin("[key_name(U)] just attempted to blow up [P] with the Detomatix cartridge and succeeded") - message_admins("[key_name_admin(U)] just attempted to blow up [P] with the Detomatix cartridge and succeeded.", 1) - detonate_act(P) - else - to_chat(U, "No charges left.") - - else - to_chat(U, "PDA not found.") - else - U.unset_machine() - ui.close() - return 0 - -//pAI FUNCTIONS=================================== - if("pai") - if(pai) - if(pai.loc != src) - pai = null - else - switch(href_list["option"]) - if("1") // Configure pAI device - pai.attack_self(U) - if("2") // Eject pAI device - var/turf/T = get_turf_or_move(src.loc) - if(T) - pai.loc = T - pai = null - + return set_ringtone() else - mode = text2num(href_list["choice"]) - if(cartridge) - cartridge.mode = mode + if(current_app) + . = current_app.Topic(href, href_list) //EXTRA FUNCTIONS=================================== - - if (mode == 2||mode == 21)//To clear message overlays. - new_message = 0 - update_icon() - - if (mode == 6||mode == 61)//To clear news overlays. - new_news = 0 - update_icon() - - if ((honkamt > 0) && (prob(60)))//For clown virus. + if((honkamt > 0) && (prob(60)))//For clown virus. honkamt-- - playsound(src, 'sound/items/bikehorn.ogg', 30, 1) + playsound(loc, 'sound/items/bikehorn.ogg', 30, 1) - return 1 // return 1 tells it to refresh the UI in NanoUI + return // return 1 tells it to refresh the UI in NanoUI /obj/item/device/pda/update_icon() ..() @@ -1230,8 +969,9 @@ var/global/list/obj/item/device/pda/PDAs = list() return if(can_use(usr)) - mode = 0 - SSnanoui.update_uis(src) + start_program(find_program(/datum/data/pda/app/main_menu)) + notifying_programs.Cut() + overlays -= image('icons/obj/pda.dmi', "pda-r") to_chat(usr, "You press the reset button on \the [src].") else to_chat(usr, "You cannot do this while restrained.") @@ -1286,8 +1026,8 @@ var/global/list/obj/item/device/pda/PDAs = list() if(ismob(loc)) var/mob/M = loc M.put_in_hands(cartridge) - mode = 0 - scanmode = 0 + // mode = 0 + // scanmode = 0 if (cartridge.radio) cartridge.radio.hostpda = null to_chat(usr, "You remove \the [cartridge] from the [name].") @@ -1363,128 +1103,132 @@ var/global/list/obj/item/device/pda/PDAs = list() return /obj/item/device/pda/attack(mob/living/C as mob, mob/living/user as mob) - if (istype(C, /mob/living/carbon)) - switch(scanmode) - if(1) + if (istype(C, /mob/living/carbon) && scanmode) + scanmode.scan_mob(C, user) - for (var/mob/O in viewers(C, null)) - O.show_message("\The [user] has analyzed [C]'s vitals!", 1) + // switch(scanmode) + // if(1) - user.show_message("Analyzing Results for [C]:") - user.show_message(" Overall Status: [C.stat > 1 ? "dead" : "[C.health - C.halloss]% healthy"]", 1) - user.show_message(text(" Damage Specifics: []-[]-[]-[]", - (C.getOxyLoss() > 50) ? "warning" : "", C.getOxyLoss(), - (C.getToxLoss() > 50) ? "warning" : "", C.getToxLoss(), - (C.getFireLoss() > 50) ? "warning" : "", C.getFireLoss(), - (C.getBruteLoss() > 50) ? "warning" : "", C.getBruteLoss() - ), 1) - user.show_message(" Key: Suffocation/Toxin/Burns/Brute", 1) - user.show_message(" Body Temperature: [C.bodytemperature-T0C]°C ([C.bodytemperature*1.8-459.67]°F)", 1) - if(C.tod && (C.stat == DEAD || (C.status_flags & FAKEDEATH))) - user.show_message(" Time of Death: [C.tod]") - if(istype(C, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = C - var/list/damaged = H.get_damaged_organs(1,1) - user.show_message("Localized Damage, Brute/Burn:",1) - if(length(damaged)>0) - for(var/obj/item/organ/external/org in damaged) - user.show_message(text(" []: []-[]", - capitalize(org.name), (org.brute_dam > 0) ? "warning" : "notice", org.brute_dam, (org.burn_dam > 0) ? "warning" : "notice", org.burn_dam),1) - else - user.show_message(" Limbs are OK.",1) + // for (var/mob/O in viewers(C, null)) + // O.show_message("\The [user] has analyzed [C]'s vitals!", 1) - if(2) - if (!istype(C:dna, /datum/dna)) - to_chat(user, "No fingerprints found on [C]") - else - to_chat(user, text("\The [C]'s Fingerprints: [md5(C:dna.uni_identity)]")) - if ( !(C:blood_DNA) ) - to_chat(user, "No blood found on [C]") - if(C:blood_DNA) - qdel(C:blood_DNA) - else - to_chat(user, "Blood found on [C]. Analysing...") - spawn(15) - for(var/blood in C:blood_DNA) - to_chat(user, "Blood type: [C:blood_DNA[blood]]\nDNA: [blood]") + // user.show_message("Analyzing Results for [C]:") + // user.show_message(" Overall Status: [C.stat > 1 ? "dead" : "[C.health - C.halloss]% healthy"]", 1) + // user.show_message(text(" Damage Specifics: []-[]-[]-[]", + // (C.getOxyLoss() > 50) ? "warning" : "", C.getOxyLoss(), + // (C.getToxLoss() > 50) ? "warning" : "", C.getToxLoss(), + // (C.getFireLoss() > 50) ? "warning" : "", C.getFireLoss(), + // (C.getBruteLoss() > 50) ? "warning" : "", C.getBruteLoss() + // ), 1) + // user.show_message(" Key: Suffocation/Toxin/Burns/Brute", 1) + // user.show_message(" Body Temperature: [C.bodytemperature-T0C]°C ([C.bodytemperature*1.8-459.67]°F)", 1) + // if(C.tod && (C.stat == DEAD || (C.status_flags & FAKEDEATH))) + // user.show_message(" Time of Death: [C.tod]") + // if(istype(C, /mob/living/carbon/human)) + // var/mob/living/carbon/human/H = C + // var/list/damaged = H.get_damaged_organs(1,1) + // user.show_message("Localized Damage, Brute/Burn:",1) + // if(length(damaged)>0) + // for(var/obj/item/organ/external/org in damaged) + // user.show_message(text(" []: []-[]", + // capitalize(org.name), (org.brute_dam > 0) ? "warning" : "notice", org.brute_dam, (org.burn_dam > 0) ? "warning" : "notice", org.burn_dam),1) + // else + // user.show_message(" Limbs are OK.",1) - if(4) - user.visible_message("\The [user] has analyzed [C]'s radiation levels!", "You have analyzed [C]'s radiation levels!") - to_chat(user, "Analyzing Results for [C]:") - if(C.radiation) - to_chat(user, "Radiation Level: [C.radiation]") - else - to_chat(user, "No radiation detected.") + // if(2) + // if (!istype(C:dna, /datum/dna)) + // to_chat(user, "No fingerprints found on [C]") + // else + // to_chat(user, text("\The [C]'s Fingerprints: [md5(C:dna.uni_identity)]")) + // if ( !(C:blood_DNA) ) + // to_chat(user, "No blood found on [C]") + // if(C:blood_DNA) + // qdel(C:blood_DNA) + // else + // to_chat(user, "Blood found on [C]. Analysing...") + // spawn(15) + // for(var/blood in C:blood_DNA) + // to_chat(user, "Blood type: [C:blood_DNA[blood]]\nDNA: [blood]") + + // if(4) + // user.visible_message("\The [user] has analyzed [C]'s radiation levels!", "You have analyzed [C]'s radiation levels!") + // to_chat(user, "Analyzing Results for [C]:") + // if(C.radiation) + // to_chat(user, "Radiation Level: [C.radiation]") + // else + // to_chat(user, "No radiation detected.") /obj/item/device/pda/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity) - if(!proximity) return - switch(scanmode) + if(proximity && scanmode) + scanmode.scan_atom(A, user) + // if(!proximity) return + // switch(scanmode) - if(3) - if(!isobj(A)) - return - if(!isnull(A.reagents)) - if(A.reagents.reagent_list.len > 0) - var/reagents_length = A.reagents.reagent_list.len - to_chat(user, "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.") - for (var/re in A.reagents.reagent_list) - to_chat(user, " [re]") - else - to_chat(user, "No active chemical agents found in [A].") - else - to_chat(user, "No significantchemical agents found in [A].") + // if(3) + // if(!isobj(A)) + // return + // if(!isnull(A.reagents)) + // if(A.reagents.reagent_list.len > 0) + // var/reagents_length = A.reagents.reagent_list.len + // to_chat(user, "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.") + // for (var/re in A.reagents.reagent_list) + // to_chat(user, " [re]") + // else + // to_chat(user, "No active chemical agents found in [A].") + // else + // to_chat(user, "No significantchemical agents found in [A].") - if(5) - analyze_gases(A, user) + // if(5) + // analyze_gases(A, user) - if (!scanmode && istype(A, /obj/item/weapon/paper) && owner) - // JMO 20140705: Makes scanned document show up properly in the notes. Not pretty for formatted documents, - // as this will clobber the HTML, but at least it lets you scan a document. You can restore the original - // notes by editing the note again. (Was going to allow you to edit, but scanned documents are too long.) - var/raw_scan = (A:info) - var/formatted_scan = "" - // Scrub out the tags (replacing a few formatting ones along the way) + // if (!scanmode && istype(A, /obj/item/weapon/paper) && owner) + // // JMO 20140705: Makes scanned document show up properly in the notes. Not pretty for formatted documents, + // // as this will clobber the HTML, but at least it lets you scan a document. You can restore the original + // // notes by editing the note again. (Was going to allow you to edit, but scanned documents are too long.) + // var/raw_scan = (A:info) + // var/formatted_scan = "" + // // Scrub out the tags (replacing a few formatting ones along the way) - // Find the beginning and end of the first tag. - var/tag_start = findtext(raw_scan,"<") - var/tag_stop = findtext(raw_scan,">") + // // Find the beginning and end of the first tag. + // var/tag_start = findtext(raw_scan,"<") + // var/tag_stop = findtext(raw_scan,">") - // Until we run out of complete tags... - while(tag_start&&tag_stop) - var/pre = copytext(raw_scan,1,tag_start) // Get the stuff that comes before the tag - var/tag = lowertext(copytext(raw_scan,tag_start+1,tag_stop)) // Get the tag so we can do intellegent replacement - var/tagend = findtext(tag," ") // Find the first space in the tag if there is one. + // // Until we run out of complete tags... + // while(tag_start&&tag_stop) + // var/pre = copytext(raw_scan,1,tag_start) // Get the stuff that comes before the tag + // var/tag = lowertext(copytext(raw_scan,tag_start+1,tag_stop)) // Get the tag so we can do intellegent replacement + // var/tagend = findtext(tag," ") // Find the first space in the tag if there is one. - // Anything that's before the tag can just be added as is. - formatted_scan = formatted_scan+pre + // // Anything that's before the tag can just be added as is. + // formatted_scan = formatted_scan+pre - // If we have a space after the tag (and presumably attributes) just crop that off. - if (tagend) - tag=copytext(tag,1,tagend) + // // If we have a space after the tag (and presumably attributes) just crop that off. + // if (tagend) + // tag=copytext(tag,1,tagend) - if (tag=="p"||tag=="/p"||tag=="br") // Check if it's I vertical space tag. - formatted_scan=formatted_scan+"
" // If so, add some padding in. + // if (tag=="p"||tag=="/p"||tag=="br") // Check if it's I vertical space tag. + // formatted_scan=formatted_scan+"
" // If so, add some padding in. - raw_scan = copytext(raw_scan,tag_stop+1) // continue on with the stuff after the tag + // raw_scan = copytext(raw_scan,tag_stop+1) // continue on with the stuff after the tag - // Look for the next tag in what's left - tag_start = findtext(raw_scan,"<") - tag_stop = findtext(raw_scan,">") + // // Look for the next tag in what's left + // tag_start = findtext(raw_scan,"<") + // tag_stop = findtext(raw_scan,">") - // Anything that is left in the page. just tack it on to the end as is - formatted_scan=formatted_scan+raw_scan + // // Anything that is left in the page. just tack it on to the end as is + // formatted_scan=formatted_scan+raw_scan - // If there is something in there already, pad it out. - if (length(note)>0) - note = note + "

" + // // If there is something in there already, pad it out. + // if (length(note)>0) + // note = note + "

" - // Store the scanned document to the notes - note = "Scanned Document. Edit to restore previous notes/delete scan.
----------
" + formatted_scan + "
" - // notehtml ISN'T set to allow user to get their old notes back. A better implementation would add a "scanned documents" - // feature to the PDA, which would better convey the availability of the feature, but this will work for now. + // // Store the scanned document to the notes + // note = "Scanned Document. Edit to restore previous notes/delete scan.
----------
" + formatted_scan + "
" + // // notehtml ISN'T set to allow user to get their old notes back. A better implementation would add a "scanned documents" + // // feature to the PDA, which would better convey the availability of the feature, but this will work for now. - // Inform the user - to_chat(user, "Paper scanned and OCRed to notekeeper.") //concept of scanning paper copyright brainoblivion 2009 + // // Inform the user + // to_chat(user, "Paper scanned and OCRed to notekeeper.") //concept of scanning paper copyright brainoblivion 2009 /obj/item/device/pda/proc/explode() //This needs tuning. //Sure did. @@ -1501,8 +1245,12 @@ var/global/list/obj/item/device/pda/PDAs = list() src.id.forceMove(get_turf(src.loc)) else QDEL_NULL(src.id) - QDEL_NULL(src.cartridge) - QDEL_NULL(src.pai) + + current_app = null + scanmode = null + QDEL_NULL(pai) + QDEL_LIST(programs) + QDEL_NULL(cartridge) return ..() /obj/item/device/pda/clown/Crossed(atom/movable/AM as mob|obj) //Clown PDA is slippery. diff --git a/code/modules/pda/pda_tgui.dm b/code/modules/pda/pda_tgui.dm new file mode 100644 index 0000000000..532c31fc80 --- /dev/null +++ b/code/modules/pda/pda_tgui.dm @@ -0,0 +1,59 @@ +// Self contained file for all things TGUI +/obj/item/device/pda/tgui_state(mob/user) + return GLOB.tgui_inventory_state + +/obj/item/device/pda/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Pda", "Personal Data Assistant") + ui.open() + +/obj/item/device/pda/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["owner"] = owner // Who is your daddy... + data["ownjob"] = ownjob // ...and what does he do? + + // update list of shortcuts, only if they changed + if(!shortcut_cache.len) + shortcut_cache = list() + shortcut_cat_order = list() + var/prog_list = programs.Copy() + if(cartridge) + prog_list |= cartridge.programs + + for(var/A in prog_list) + var/datum/data/pda/P = A + + if(P.hidden) + continue + var/list/cat + if(P.category in shortcut_cache) + cat = shortcut_cache[P.category] + else + cat = list() + shortcut_cache[P.category] = cat + shortcut_cat_order += P.category + cat |= list(list(name = P.name, icon = P.icon, notify_icon = P.notify_icon, ref = "\ref[P]")) + + // force the order of a few core categories + shortcut_cat_order = list("General") \ + + sortList(shortcut_cat_order - list("General", "Scanners", "Utilities")) \ + + list("Scanners", "Utilities") + + data["idInserted"] = (id ? 1 : 0) + data["idLink"] = (id ? text("[id.registered_name], [id.assignment]") : "--------") + + data["useRetro"] = retro_mode + + data["cartridge_name"] = cartridge ? cartridge.name : "" + data["stationTime"] = worldtime2stationtime(world.time) + + data["app"] = list( + "name" = current_app.title, + "icon" = current_app.icon, + "template" = current_app.template, + "has_back" = current_app.has_back) + current_app.update_ui(user, data) + + return data \ No newline at end of file diff --git a/code/modules/pda/utilities.dm b/code/modules/pda/utilities.dm new file mode 100644 index 0000000000..dee706b88a --- /dev/null +++ b/code/modules/pda/utilities.dm @@ -0,0 +1,200 @@ +/datum/data/pda/utility/flashlight + name = "Enable Flashlight" + icon = "lightbulb-o" + + var/fon = 0 //Is the flashlight function on? + var/f_lum = 2 //Luminosity for the flashlight function + +/datum/data/pda/utility/flashlight/start() + fon = !fon + name = fon ? "Disable Flashlight" : "Enable Flashlight" + pda.update_shortcuts() + pda.set_light(fon ? f_lum : 0) + +/datum/data/pda/utility/honk + name = "Honk Synthesizer" + icon = "smile-o" + category = "Clown" + + var/last_honk //Also no honk spamming that's bad too + +/datum/data/pda/utility/honk/start() + if(!(last_honk && world.time < last_honk + 20)) + playsound(pda.loc, 'sound/items/bikehorn.ogg', 50, 1) + last_honk = world.time + +/datum/data/pda/utility/toggle_door + name = "Toggle Door" + icon = "external-link" + var/remote_door_id = "" + +// /datum/data/pda/utility/toggle_door/start() +// for(var/obj/machinery/door/poddoor/M in airlocks) +// if(M.id_tag == remote_door_id) +// if(M.density) +// M.open() +// else +// M.close() + +/datum/data/pda/utility/scanmode/medical + base_name = "Med Scanner" + icon = "heart-o" + +/datum/data/pda/utility/scanmode/medical/scan_mob(mob/living/C as mob, mob/living/user as mob) + C.visible_message("[user] has analyzed [C]'s vitals!") + + user.show_message("Analyzing Results for [C]:") + user.show_message("\t Overall Status: [C.stat > 1 ? "dead" : "[C.health]% healthy"]", 1) + user.show_message("\t Damage Specifics: [C.getOxyLoss() > 50 ? "" : ""][C.getOxyLoss()]-[C.getToxLoss() > 50 ? "" : ""][C.getToxLoss()]-[C.getFireLoss() > 50 ? "" : ""][C.getFireLoss()]-[C.getBruteLoss() > 50 ? "" : ""][C.getBruteLoss()]", 1) + user.show_message("\t Key: Suffocation/Toxin/Burns/Brute", 1) + user.show_message("\t Body Temperature: [C.bodytemperature-T0C]°C ([C.bodytemperature*1.8-459.67]°F)", 1) + if(C.tod && (C.stat == DEAD || (C.status_flags & FAKEDEATH))) + user.show_message("\t Time of Death: [C.tod]") + if(istype(C, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = C + var/list/damaged = H.get_damaged_organs(1,1) + user.show_message("Localized Damage, Brute/Burn:",1) + if(length(damaged)>0) + for(var/obj/item/organ/external/org in damaged) + user.show_message("\t [capitalize(org.name)]: [org.brute_dam > 0 ? "[org.brute_dam]" : "0"]-[org.burn_dam > 0 ? "[org.burn_dam]" : "0"]", 1) + else + user.show_message("\t Limbs are OK.",1) + +/datum/data/pda/utility/scanmode/dna + base_name = "DNA Scanner" + icon = "link" + +/datum/data/pda/utility/scanmode/dna/scan_mob(mob/living/C as mob, mob/living/user as mob) + if(istype(C, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = C + if(!istype(H.dna, /datum/dna)) + to_chat(user, "No fingerprints found on [H]") + else + to_chat(user, "[H]'s Fingerprints: [md5(H.dna.uni_identity)]") + scan_blood(C, user) + +/datum/data/pda/utility/scanmode/dna/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob) + scan_blood(A, user) + +/datum/data/pda/utility/scanmode/dna/proc/scan_blood(atom/A, mob/user) + if(!A.blood_DNA) + to_chat(user, "No blood found on [A]") + if(A.blood_DNA) + qdel(A.blood_DNA) + else + to_chat(user, "Blood found on [A]. Analysing...") + spawn(15) + for(var/blood in A.blood_DNA) + to_chat(user, "Blood type: [A.blood_DNA[blood]]\nDNA: [blood]") + +/datum/data/pda/utility/scanmode/halogen + base_name = "Halogen Counter" + icon = "exclamation-circle" + +/datum/data/pda/utility/scanmode/halogen/scan_mob(mob/living/C as mob, mob/living/user as mob) + C.visible_message("[user] has analyzed [C]'s radiation levels!") + + user.show_message("Analyzing Results for [C]:") + if(C.radiation) + user.show_message("Radiation Level: [C.radiation > 0 ? "[C.radiation]" : "0"]") + else + user.show_message("No radiation detected.") + +/datum/data/pda/utility/scanmode/reagent + base_name = "Reagent Scanner" + icon = "flask" + +/datum/data/pda/utility/scanmode/reagent/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob) + if(!isnull(A.reagents)) + if(A.reagents.reagent_list.len > 0) + var/reagents_length = A.reagents.reagent_list.len + to_chat(user, "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.") + for(var/re in A.reagents.reagent_list) + to_chat(user, "\t [re]") + else + to_chat(user, "No active chemical agents found in [A].") + else + to_chat(user, "No significant chemical agents found in [A].") + +/datum/data/pda/utility/scanmode/gas + base_name = "Gas Scanner" + icon = "tachometer" + +/datum/data/pda/utility/scanmode/gas/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob) + // if(istype(A, /obj/item/tank)) + // var/obj/item/tank/T = A + // pda.atmosanalyzer_scan(T.air_contents, user, T) + // else if(istype(A, /obj/machinery/portable_atmospherics)) + // var/obj/machinery/portable_atmospherics/T = A + // pda.atmosanalyzer_scan(T.air_contents, user, T) + // else if(istype(A, /obj/machinery/atmospherics/pipe)) + // var/obj/machinery/atmospherics/pipe/T = A + // pda.atmosanalyzer_scan(T.parent.air, user, T) + // else if(istype(A, /obj/machinery/power/rad_collector)) + // var/obj/machinery/power/rad_collector/T = A + // if(T.P) + // pda.atmosanalyzer_scan(T.P.air_contents, user, T) + // else if(istype(A, /obj/item/flamethrower)) + // var/obj/item/flamethrower/T = A + // if(T.ptank) + // pda.atmosanalyzer_scan(T.ptank.air_contents, user, T) + // else if(istype(A, /obj/machinery/portable_atmospherics/scrubber/huge)) + // var/obj/machinery/portable_atmospherics/scrubber/huge/T = A + // pda.atmosanalyzer_scan(T.air_contents, user, T) + // else if(istype(A, /obj/machinery/atmospherics/unary/tank)) + // var/obj/machinery/atmospherics/unary/tank/T = A + // pda.atmosanalyzer_scan(T.air_contents, user, T) + +/datum/data/pda/utility/scanmode/notes + base_name = "Note Scanner" + icon = "clipboard" + var/datum/data/pda/app/notekeeper/notes + +/datum/data/pda/utility/scanmode/notes/start() + . = ..() + notes = pda.find_program(/datum/data/pda/app/notekeeper) + +/datum/data/pda/utility/scanmode/notes/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob) + if(notes && istype(A, /obj/item/weapon/paper)) + var/obj/item/weapon/paper/P = A + var/list/brlist = list("p", "/p", "br", "hr", "h1", "h2", "h3", "h4", "/h1", "/h2", "/h3", "/h4") + + // JMO 20140705: Makes scanned document show up properly in the notes. Not pretty for formatted documents, + // as this will clobber the HTML, but at least it lets you scan a document. You can restore the original + // notes by editing the note again. (Was going to allow you to edit, but scanned documents are too long.) + var/raw_scan = sanitize_simple(P.info, list("\t" = "", "ÿ" = "")) + var/formatted_scan = "" + // Scrub out the tags (replacing a few formatting ones along the way) + // Find the beginning and end of the first tag. + var/tag_start = findtext(raw_scan, "<") + var/tag_stop = findtext(raw_scan, ">") + // Until we run out of complete tags... + while(tag_start && tag_stop) + var/pre = copytext(raw_scan, 1, tag_start) // Get the stuff that comes before the tag + var/tag = lowertext(copytext(raw_scan, tag_start + 1, tag_stop)) // Get the tag so we can do intellegent replacement + var/tagend = findtext(tag, " ") // Find the first space in the tag if there is one. + // Anything that's before the tag can just be added as is. + formatted_scan = formatted_scan + pre + // If we have a space after the tag (and presumably attributes) just crop that off. + if(tagend) + tag = copytext(tag, 1, tagend) + if(tag in brlist) // Check if it's I vertical space tag. + formatted_scan = formatted_scan + "
" // If so, add some padding in. + raw_scan = copytext(raw_scan, tag_stop + 1) // continue on with the stuff after the tag + // Look for the next tag in what's left + tag_start = findtext(raw_scan, "<") + tag_stop = findtext(raw_scan, ">") + // Anything that is left in the page. just tack it on to the end as is + formatted_scan = formatted_scan + raw_scan + // If there is something in there already, pad it out. + if(length(notes.note) > 0) + notes.note += "

" + // Store the scanned document to the notes + notes.note += "Scanned Document. Edit to restore previous notes/delete scan.
----------
" + formatted_scan + "
" + // notehtml ISN'T set to allow user to get their old notes back. A better implementation would add a "scanned documents" + // feature to the PDA, which would better convey the availability of the feature, but this will work for now. + // Inform the user + to_chat(user, "Paper scanned and OCRed to notekeeper.")//concept of scanning paper copyright brainoblivion 2009 + + else + to_chat(user, "Error scanning [A].") diff --git a/nano/css/shared.css b/nano/css/shared.css index 3517402e2a..83d1e85cc3 100644 --- a/nano/css/shared.css +++ b/nano/css/shared.css @@ -48,13 +48,13 @@ hr { .link32 { float: left; min-width: 15px; - max-width: 31px; + max-width: 31px; height: 31px; text-align: center; - text-decoration: none; - background: #40628a; + text-decoration: none; + background: #40628a; border: 1px solid #161616; - margin: 0 2px 2px 0; + margin: 0 2px 2px 0; cursor: default; } @@ -191,7 +191,7 @@ h4 { } .greenButton { - background: #00aa00; + background: #00aa00; } .yellowButton { @@ -209,11 +209,11 @@ h4 { .caption { font-size: 10px; font-weight: bold; - padding: 5px; + padding: 5px; } .footer { - font-size: 10px; + font-size: 10px; } .multiLine { @@ -267,7 +267,7 @@ div.notice { width: 100%; margin: 4px 0 0 0; clear: both; - overflow: auto; + overflow: auto; } .itemContentNarrow, .itemContent, .itemContentWide, .itemContentSmall, .itemContentMedium { @@ -325,7 +325,7 @@ div.notice { border: 1px solid #40628a; padding: 4px; margin: 3px 0; - overflow: hidden; + overflow: hidden; } .statusDisplayRecords { @@ -339,16 +339,16 @@ div.notice { } .statusDisplayComm { - width: 100%; + width: 100%; background: rgba(0, 0, 0, 0.5); color: #ffffff; - padding-bottom: 4px; - border-top: 0 none; - border-left: 0 none; - border-right: 0 none; - border-bottom: 2px inset #40628a; + padding-bottom: 4px; + border-top: 0 none; + border-left: 0 none; + border-right: 0 none; + border-bottom: 2px inset #40628a; margin: 3px 0; - overflow: hidden; + overflow: hidden; } .statusLabel { @@ -410,7 +410,7 @@ div.notice { float: left; margin: 0 5px 0 0; overflow: hidden; - background: #40628a; + background: #40628a; } .displayBarVerticalFill { @@ -418,7 +418,7 @@ div.notice { height: 0%; overflow: hidden; float: left; - background: #000000; + background: #000000; } /*used in xenobio2 computer*/ @@ -428,12 +428,12 @@ div.notice { width: 100px; height: 110px; border-right: 2px solid rgb(102, 179, 255); - border-left: 2px solid rgb(102, 179, 255); - border-bottom: 2px solid rgb(102, 179, 255); + border-left: 2px solid rgb(102, 179, 255); + border-bottom: 2px solid rgb(102, 179, 255); float: left; margin: 0 5px 0 0; overflow: hidden; - background: #40628a; + background: #40628a; } .displayBarBeakerFill { @@ -441,7 +441,7 @@ div.notice { height: 0%; overflow: hidden; float: left; - background: rgba(153, 204, 255, 0.3); + background: rgba(153, 204, 255, 0.3); } .displayBarFill.alignRight { @@ -514,7 +514,7 @@ div.notice { .fixedLeftWiderRed { width: 220px; float: left; - background: #ee0000; + background: #ee0000; } .floatRight { @@ -522,7 +522,7 @@ div.notice { } .floatLeft { - float: left; + float: left; } /* Used in PDA */ @@ -538,6 +538,14 @@ div.notice { .pdalink { float: left; white-space: nowrap; + cursor: default !important; +} + +.pdalink.disabled { + background: none; + border: none; + color: #999; + text-align: left; } /* DNA Modifier UI (dna_modifier.tmpl) */ @@ -585,11 +593,11 @@ div.notice { } table.fixed { - table-layout:fixed; + table-layout:fixed; } table.fixed td { - overflow: hidden; + overflow: hidden; } /* Table stuffs for power monitor */ @@ -641,9 +649,9 @@ th.Cargo { } th.Planetside{ - background: #555555; - font-weight: bold; - color: #ffffff; + background: #555555; + font-weight: bold; + color: #ffffff; } th.Civilian { @@ -659,9 +667,9 @@ th.Miscellaneous { } th.Silicon { - background: #222222; - font-weight: bold; - color: #ffffff; + background: #222222; + font-weight: bold; + color: #ffffff; } /* Damage/health colors for crew monitoring computer */ @@ -696,20 +704,20 @@ th.Silicon { /*Communicator Homescreen*/ div.homeContainer { - width: 100%; - text-align: center; - margin: auto; - padding-top: 5px; + width: 100%; + text-align: center; + margin: auto; + padding-top: 5px; } div.homeScreen { - padding: 0 20px 0 20px; + padding: 0 20px 0 20px; } .link64 { float: left; min-width: 15px; - max-width: 68px; + max-width: 68px; height: 64px; text-align: center; color: #ffffff; @@ -717,43 +725,43 @@ div.homeScreen { background: rgba(64, 98, 138, 0.6); border: 1px solid #161616; padding: 0 0 4px 0; - margin: 0 0 30px 5.5%; + margin: 0 0 30px 5.5%; cursor: default; - border-radius: 10px; + border-radius: 10px; } .link64:hover { - font-size: 13px; + font-size: 13px; } /*Communicator Dialing*/ .dialPad { clear: both; - text-align: center; - margin: auto; + text-align: center; + margin: auto; } .dialPadKeys { float: left; - width: 47px; - height: 47px; - font-size: 32px; - text-align: center; + width: 47px; + height: 47px; + font-size: 32px; + text-align: center; color: #ffffff; text-decoration: none; background: rgba(64, 98, 138, 0.15); border: 1px inset rgba(100, 100, 100, 0.25); cursor: default; - line-height: 47px; + line-height: 47px; } .dialPadKeys:hover { - background: rgba(80, 122, 172, 0.5); + background: rgba(80, 122, 172, 0.5); } .dialPadButtons { float: left; - width: 63.2px; + width: 63.2px; height: 63.2px; text-align: center; color: #ffffff; @@ -762,42 +770,88 @@ div.homeScreen { border: 1px inset rgba(100, 100, 100, 0.25); padding: 0 0 4px 0; cursor: default; - margin-bottom: 25px; + margin-bottom: 25px; } .dialPadButtons:hover { - background: rgba(80, 122, 172, 0.5); - font-size: 13px; - + background: rgba(80, 122, 172, 0.5); + font-size: 13px; + } .dialPadAddress { float: left; - width: 192px; - height: 18px; + width: 192px; + height: 18px; background: rgba(64, 98, 138, 0.15); - color: #ffffff; - border-left: 1px inset #40628a; - border-top: 1px inset #40628a; - border-bottom: 1px inset #40628a; + color: #ffffff; + border-left: 1px inset #40628a; + border-top: 1px inset #40628a; + border-bottom: 1px inset #40628a; } .dialPadAddress:hover{ - background: rgba(80, 122, 172, 0.5); + background: rgba(80, 122, 172, 0.5); } .dialPadClear { float: left; - width: 19%; - height: 18px; + width: 19%; + height: 18px; background: rgba(64, 98, 138, 0.15); - color: #ffffff; - border-right: 1px inset #40628a; - border-top: 1px inset #40628a; - border-bottom: 1px inset #40628a; + color: #ffffff; + border-right: 1px inset #40628a; + border-top: 1px inset #40628a; + border-bottom: 1px inset #40628a; } .dialPadClear:hover{ - background: rgba(80, 122, 172, 0.5); -} \ No newline at end of file + background: rgba(80, 122, 172, 0.5); +} + + +.item.mainmenu { + padding-bottom: 0.5em; +} + +div.clock { + color: white; + font-weight: bold; + float: right; + padding-right: 2em; +} + +#pdaFooter { + position: fixed; + left: 0; + bottom: 0; + width: 100%; + background: #2c2c2c; +} + +.pdaFooterButton { + width: 33%; + padding: 0.5em 0; + font-size: 200%; + border: none; + background: none; + color: #fff; +} + +.pdaFooterButton:hover { + background: #507aac; +} + +.pdaFooterButton.disabled { + background: none !important; + color: #555 !important; +} + +.pdaFooterButton.uiLinkPendingIcon { + position: absolute; + width: 33%; + background-repeat: no-repeat; + background-position: center; +} + diff --git a/nano/js/nano_state_pda.js b/nano/js/nano_state_pda.js new file mode 100644 index 0000000000..a27da2e1c1 --- /dev/null +++ b/nano/js/nano_state_pda.js @@ -0,0 +1,51 @@ +NanoStatePDAClass.inheritsFrom(NanoStateClass); +var NanoStatePDA = new NanoStatePDAClass(); + +function NanoStatePDAClass() { + this.key = 'pda'; + this.key = this.key.toLowerCase(); + this.current_template = ""; + + NanoStateManager.addState(this); +} + +NanoStatePDAClass.prototype.onUpdate = function(data) { + NanoStateClass.prototype.onUpdate.call(this, data); + var state = this; + + try { + if(data['data']['app'] != null) { + var template = data['data']['app']['template']; + if(template != null && template != state.current_template) { + var templateMarkup = nanouiTemplateBundle()[template + ".tmpl"]; + if (templateMarkup) { + templateMarkup += '
'; + + try { + NanoTemplate.addTemplate('app', templateMarkup); + NanoTemplate.resetTemplate('app'); + $("#uiApp").html(NanoTemplate.parse('app', data)); + state.current_template = template; + + if(data['config']['status'] == 2) { + $('#uiApp .linkActive').addClass('linkPending'); + $('#uiApp .linkActive').oneTime(400, 'linkPending', function () { + $('#uiApp .linkActive').removeClass('linkPending inactive'); + }); + } + } catch(error) { + reportError('ERROR: An error occurred while loading the PDA App UI: ' + error.message); + return; + } + } + } else { + if (NanoTemplate.templateExists('app')) { + $("#uiApp").html(NanoTemplate.parse('app', data)); + } + } + } + } catch(error) { + reportError('ERROR: An error occurred while rendering the PDA App UI: ' + error.message); + return; + } +} \ No newline at end of file diff --git a/nano/js/nano_template.js b/nano/js/nano_template.js index b50df1281a..a8b095da7c 100644 --- a/nano/js/nano_template.js +++ b/nano/js/nano_template.js @@ -76,6 +76,9 @@ var NanoTemplate = function () { templateExists: function (key) { return _templates.hasOwnProperty(key); }, + resetTemplate: function (key) { + _compiledTemplates[key] = null; + }, parse: function (templateKey, data) { if (!_compiledTemplates.hasOwnProperty(templateKey) || !_compiledTemplates[templateKey]) { if (!_templates.hasOwnProperty(templateKey)) { diff --git a/nano/js/nano_utility.js b/nano/js/nano_utility.js index e65b7bbd13..879d340992 100644 --- a/nano/js/nano_utility.js +++ b/nano/js/nano_utility.js @@ -166,4 +166,9 @@ if (!String.prototype.ckey) { String.prototype.ckey = function () { return this.replace(/\W/g, '').toLowerCase(); }; +} + +var reportError = function (str) { + window.location = "byond://?nano_err=" + encodeURIComponent(str); + alert(str); } \ No newline at end of file diff --git a/nano/templates/pda.tmpl b/nano/templates/pda.tmpl index 3177450ba6..61579d0fb6 100644 --- a/nano/templates/pda.tmpl +++ b/nano/templates/pda.tmpl @@ -1,784 +1,156 @@ - +{{if data.useRetro}} + + + +{{/if}} {{if data.owner}}
-
- Functions: -
-
- - {{:helper.link('Close', 'gear', {'choice' : "Close"}, null, 'fixedLeft')}} - {{if data.idInserted}} {{:helper.link('Update PDA Info', 'eject', {'choice' : "UpdateInfo"}, null, 'fixedLeftWide')}} {{/if}} - {{if data.mode != 0}} {{:helper.link('Return', 'arrowreturn-1-w', {'choice' : "Return"}, null, 'fixedLeft')}} {{/if}} +
+ {{if data.idInserted}} + {{:helper.link(data.idLink, 'eject', {'choice' : "Authenticate"}, null, 'pdalink fixedLeftWide')}} + {{/if}} + {{if data.cartridge_name}} + {{:helper.link(data.cartridge_name, 'eject', {'choice' : "Eject"}, null, 'pdalink fixedLeftWidest')}} + {{/if}} + {{:helper.link('Toggle R.E.T.R.O. mode', 'gear', {'choice': "Retro"}, null, 'pdalink fixedLeftWide')}} +
+
+ {{:data.stationTime}}
-
-
-
- Station Time: -
-
- {{:data.stationTime}} -
+ + {{if data.app}} +

 {{:data.app.name}}

+
+
 
+ {{/if}} + +
+ {{:helper.link("", 'back', {'choice' : "Back"}, data.app.has_back ? null : 'disabled', 'link pdaFooterButton')}} + {{:helper.link("", 'home', {'choice' : "Home"}, data.app.is_home ? 'disabled' : null, 'link pdaFooterButton')}}
-
- - - {{if data.mode == 0}} -
-
- Owner: -
-
- {{:data.owner}}, {{:data.ownjob}} -
-
-
-
-
- ID: -
-
- {{:helper.link(data.idLink, 'eject', {'choice' : "Authenticate"}, data.idInserted ? null : 'disabled', data.idInserted ? 'floatright' : 'fixedLeft')}} -
-
-
-
-
- Cartridge: -
-
- {{if data.cart_loaded==1}} - {{:helper.link(data.cartridge.name, 'eject', {'choice' : "Eject"},null,null)}} - {{else}} - {{:helper.link('None', 'eject', {'choice' : "Eject"},'disabled',null)}} - {{/if}} -
-
-
-

Functions

-
-
-
- General: -
-
- {{:helper.link('Notekeeper', 'note', {'choice' : "1"}, null, 'fixedLeftWide')}} - {{:helper.link('Messenger', data.new_Message ? 'mail-closed' : 'mail-open', {'choice' : "2"}, null, 'fixedLeftWide')}} - {{:helper.link('Crew Manifest', 'contact', {'choice' : "41"}, null, 'fixedLeftWide')}} - {{:helper.link('News', data.new_News ? 'mail-closed' : 'mail-open', {'choice' : "6"}, null, 'fixedLeftWide')}} -
-
-
- {{if data.cartridge}} - {{if data.cartridge.access.access_clown == 1}} -
-
- Clown: -
-
- {{:helper.link('Honk Synthesizer', 'gear', {'choice' : "Honk"}, null, 'fixedLeftWide')}} -
-
-
- {{/if}} - {{if data.cartridge.access.access_engine == 1}} -
-
- Engineering: -
-
- {{:helper.link('Power Monitor', 'alert', {'choice' : "43"}, null, 'fixedLeftWide')}} -
-
-
- {{/if}} - {{if data.cartridge.access.access_medical == 1}} -
-
- Medical: -
-
- {{:helper.link('Medical Records', 'gear', {'choice' : "44"}, null, 'fixedLeftWide')}} - {{:helper.link(data.scanmode == 1 ? 'Disable Med Scanner' : 'Enable Med Scanner', 'gear', {'choice' : "Medical Scan"}, null , 'fixedLeftWide')}} -
-
-
- {{/if}} - {{if data.cartridge.access.access_security == 1}} -
-
- Security: -
-
- {{:helper.link('Security Records', 'gear', {'choice' : "45"}, null, 'fixedLeftWide')}} - {{if data.cartridge.radio ==1}} {{:helper.link('Security Bot Access', 'gear', {'choice' : "46"}, null, 'fixedLeftWide')}} {{/if}} -
-
-
-
- {{/if}} - {{if data.cartridge.access.access_quartermaster == 1}} -
-
- Quartermaster: -
-
- {{:helper.link('Supply Records', 'gear', {'choice' : "47"}, null, 'fixedLeftWide')}} - {{:helper.link('Delivery Bot Control', 'gear', {'choice' : "48"}, null, 'fixedLeftWide')}} -
-
-
-
- {{/if}} - {{/if}} -
-
-
- Utilities: -
-
- {{if data.cartridge}} - {{if data.cartridge.access.access_status_display == 1}} - {{:helper.link('Status Display', 'gear', {'choice' : "42"}, null, 'fixedLeftWide')}} - {{/if}} - {{if data.cartridge.access.access_janitor==1}} - {{:helper.link('Custodial Locator', 'gear', {'choice' : "49"}, null, 'fixedLeftWide')}} - {{/if}} - {{if data.cartridge.radio == 2}} - {{:helper.link('Signaler System', 'gear', {'choice' : "40"}, null, 'fixedLeftWide')}} - {{/if}} - {{if data.cartridge.access.access_reagent_scanner==1}} - {{:helper.link(data.scanmode == 3 ? 'Disable Reagent Scanner' : 'Enable Reagent Scanner', 'gear', {'choice' : "Reagent Scan"}, null, 'fixedLeftWider')}} - {{/if}} - {{if data.cartridge.access.access_engine==1}} - {{:helper.link(data.scanmode == 4 ? 'Disable Halogen Counter' : 'Enable Halogen Counter', 'gear', {'choice' : "Halogen Counter"}, null, 'fixedLeftWider')}} - {{/if}} - {{if data.cartridge.access.access_atmos==1}} - {{:helper.link(data.scanmode == 5 ? 'Disable Gas Scanner' : 'Enable Gas Scanner', 'gear', {'choice' : "Gas Scan"}, null, 'fixedLeftWide')}} - {{/if}} - {{if data.cartridge.access.access_remote_door==1}} - {{:helper.link('Toggle Door', 'gear', {'choice' : "Toggle Door"}, null, 'fixedLeftWide')}} - {{/if}} - {{/if}} - {{:helper.link('Atmospheric Scan', 'gear', {'choice' : "3"}, null, 'fixedLeftWide')}} - {{:helper.link(data.touch_silent==1 ? 'Enable Beeping' : 'Disable Beeping', 'gear', {'choice' : "Toggle Beeping"}, null,'fixedLeftWide')}} - {{:helper.link(data.fon==1 ? 'Disable Flashlight' : 'Enable Flashlight', 'lightbulb', {'choice' : "Light"}, null,'fixedLeftWide')}} -
-
- {{if data.pai}} -
-
- PAI Utilities: -
-
- {{:helper.link('Configuration', 'gear', {'choice' : "pai", 'option' : "1"}, null, 'fixedLeft')}} - {{:helper.link('Eject pAI', 'eject', {'choice' : "pai", 'option' : "2"}, null, 'fixedLeft')}} -
-
- {{/if}} - - - {{else data.mode == 1}} -
-
- Notes: -
-
-
-
-
- {{:data.note}} -
-
-
-
-
- {{:helper.link('Edit Notes', 'gear', {'choice' : "Edit"}, null, 'fixedLeft')}} -
-
- - - {{else data.mode == 2}} -

SpaceMessenger V4.0.1

-
-
- Messenger Functions: -
-
- {{:helper.link(data.message_silent==1 ? 'Ringer: Off' : 'Ringer: On', data.message_silent==1 ? 'volume-off' : 'volume-on', {'choice' : "Toggle Ringer"}, null, 'fixedLeftWide')}} - {{:helper.link(data.toff==1 ? 'Messenger: Off' : 'Messenger: On',data.toff==1 ? 'close':'check', {'choice' : "Toggle Messenger"}, null, 'fixedLeftWide')}} - {{:helper.link('Set Ringtone', 'comment', {'choice' : "Ringtone"}, null, 'fixedLeftWide')}} - {{:helper.link('Delete all Conversations', 'trash', {'choice' : "Clear", 'option' : "All"}, null, 'fixedLeftWider')}} -
-
- {{if data.toff == 0}} -

- {{if data.cartridge}} - {{if data.cartridge.charges}} -
- {{:data.cartridge.charges}} - {{if data.cartridge.access.access_detonate_pda}} detonation charges left. {{/if}} - {{if data.cartridge.access.access_clown || data.cartridge.access.access_mime}} viral files left. {{/if}} - -

-
- {{/if}} - {{/if}} - - {{if data.pda_count == 0}} - No other PDAS located - {{else}} -

Current Conversations

- {{for data.convopdas}} -
- {{:helper.link(value.Name, 'circle-arrow-s', {'choice' : "Select Conversation", 'convo' : value.Reference } , null, value.fixedLeftWider)}} - {{if data.cartridge}} - {{if data.cartridge.access.access_detonate_pda && value.Detonate}} - {{:helper.link('*Detonate*', 'radiation', {'choice' : "Detonate", 'target' : value.Reference}, null, 'fixedLeft')}} - {{/if}} - {{if data.cartridge.access.access_clown}} - {{:helper.link('*Send Virus*', 'star', {'choice' : "Send Honk", 'target' : value.Reference}, null, 'fixedLeft')}} - {{/if}} - {{if data.cartridge.access.access_mime}} - {{:helper.link('*Send Virus*', 'circle-arrow-s', {'choice' : "Send Silence", 'target' : value.Reference}, null, 'fixedLeft')}} - {{/if}} - {{/if}} -
- {{/for}} -

Other PDAs

- {{for data.pdas}} -
- {{:helper.link(value.Name, 'circle-arrow-s', {'choice' : "Message", 'target' : value.Reference}, null, value.fixedLeftWider)}} - {{if data.cartridge}} - {{if data.cartridge.access.access_detonate_pda && value.Detonate}} {{:helper.link('*Detonate*', 'radiation', {'choice' : "Detonate", 'target' : value.Reference}, null, 'fixedLeft')}} {{/if}} - {{if data.cartridge.access.access_clown}} {{:helper.link('*Send Virus*', 'star', {'choice' : "Send Honk", 'target' : value.Reference}, null, 'fixedLeft')}} {{/if}} - {{if data.cartridge.access.access_mime}} {{:helper.link('*Send Virus*', 'circle-arrow-s', {'choice' : "Send Silence", 'target' : value.Reference}, null, 'fixedLeft')}} {{/if}} - {{/if}} -
- {{/for}} - {{/if}} - {{/if}} - - - {{else data.mode == 21}} -

SpaceMessenger V4.0.1

-
-
- Messenger Functions: -
-
- {{:helper.link('Delete Conversation', 'trash', {'choice' : "Clear", 'option' : "Convo"}, null, 'fixedLeftWide')}} -
-
-
-
-

Conversation with: {{:data.convo_name}} ({{:data.convo_job}})

-
-
-
- {{for data.messages}} - {{if data.active_conversation == value.target}} - {{if value.sent==0}} - Them: {{:value.message}}
- {{else}} - You: {{:value.message}}
- {{/if}} - {{/if}} - {{/for}} -
-
-
- {{:helper.link('Reply', 'comment', {'choice' : "Message", 'target': data.active_conversation}, null, 'fixedLeft')}} - - - {{else data.mode== 41}} - {{#def.crewManifest}} - - - {{else data.mode == 3}} -

Atmospheric Scan

- {{#def.atmosphericScan}} - - {{else data.mode == 40}} -

Remote Signaling System

-
-
- Frequency: -
-
- {{:data.records.signal_freq}} -
-   - {{:helper.link('-1', null, {'cartmenu' : "1", 'choice' : "Signal Frequency", 'sfreq' : "-10"}, null, null)}}  - {{:helper.link('-.2', null, {'cartmenu' : "1", 'choice' : "Signal Frequency", 'sfreq' : "-2"}, null, null)}}  - - {{:helper.link('+.2', null, {'cartmenu' : "1", 'choice' : "Signal Frequency", 'sfreq' : "2"}, null, null)}}  - {{:helper.link('+1', null, {'cartmenu' : "1", 'choice' : "Signal Frequency", 'sfreq' : "10"}, null, null)}} -
-
-
-
-
-
- Code: -
-
- - {{:data.records.signal_code}}
-
- {{:helper.link('-5', null, {'cartmenu' : "1", 'choice' : "Signal Code", 'scode' : "-5"}, null, null)}} - {{:helper.link('-1', null, {'cartmenu' : "1", 'choice' : "Signal Code", 'scode' : "-1"}, null, null)}} - {{:helper.link('+1', null, {'cartmenu' : "1", 'choice' : "Signal Code", 'scode' : "1"}, null, null)}} - {{:helper.link('+5', null, {'cartmenu' : "1", 'choice' : "Signal Code", 'scode' : "5"}, null, null)}} -
-
-
- {{:helper.link('Send Signal', 'radiation', {'cartmenu' : "1", 'choice' : "Send Signal"}, null, null)}} -
- - - {{else data.mode == 42}} -

Station Status Displays Interlink

-
-
- Code: -
-
- {{:helper.link('Clear', 'trash', {'cartmenu' : "1", 'choice' : "Status", 'statdisp' : "blank"}, null, null)}} - - {{:helper.link('Tram ETA', 'gear', {'cartmenu' : "1", 'choice' : "Status",'statdisp' : "shuttle"}, null, null)}} - {{:helper.link('Message', 'gear', {'cartmenu' : "1", 'choice' : "Status",'statdisp' : "message"}, null, null)}} -
-
-
-
-
- Message line 1 -
-
- {{:helper.link(data.records.message1 + ' (set)', 'pencil', {'cartmenu' : "1", 'choice' : "Status",'statdisp' : "setmsg1"}, null, null)}} -
-
-
-
- Message line 2 -
-
- {{:helper.link(data.records.message2 + ' (set)', 'pencil', {'cartmenu' : "1", 'choice' : "Status",'statdisp' : "setmsg2"}, null, null)}} -
-
- -
-
-
- ALERT!: -
-
- {{:helper.link('None', 'alert', {'cartmenu' : "1", 'choice' : "Status",'statdisp' : "alert", 'alert' : "default"}, null, null)}} - {{:helper.link('Red Alert', 'alert', {'cartmenu' : "1", 'choice' : "Status",'statdisp' : "alert", 'alert' : "redalert"}, null, null)}} - {{:helper.link('Lockdown', 'caution', {'cartmenu' : "1", 'choice' : "Status",'statdisp' : "alert", 'alert' : "lockdown"}, null, null)}} - {{:helper.link('Biohazard', 'radiation', {'cartmenu' : "1", 'choice' : "Status",'statdisp' : "alert", 'alert' : "biohazard"}, null, null)}} -
-
- - {{else data.mode == 43}} -

Sensor Selection

-
- Available Sensors: -
- {{for data.records.power_sensors}} -
- {{:helper.link(value.name_tag, 'plus', {'cartmenu' : "1", 'choice' : "Power Select",'target' : value.name_tag})}}
-
- {{/for}} - - - {{else data.mode == 433}} -

Sensor Reading(Simplified View)

- {{if data.records.sensor_reading}} -
AreaCell %Load - {{for data.records.sensor_reading.apc_data}} -
{{:value.name}} - {{:value.cell_charge}}% - {{:value.total_load}} - {{empty}} -
No APCs found! - {{/for}} -
-
Available: {{:data.records.sensor_reading.total_avail}} -
Load: {{:data.records.sensor_reading.total_used_all}} - {{else}} - Unable to contact sensor controller! Please retry and contact tech support if problem persists. - {{/if}} - - {{else data.mode == 44}} -

Medical Record List

-
- Select A record -
-
- {{for data.records.medical_records}} -
- {{:helper.link(value.Name, 'gear', {'cartmenu' : "1", 'choice' : "Medical Records",'target' : value.ref}, null, null)}} -
- {{/for}} - - - {{else data.mode == 441}} -

Medical Record

-
-
-
- {{if data.records.general_exists == 1}} - Name: {{:data.records.general.name}}
- Entity Class: {{:data.records.general.brain_type}}
- Sex: {{:data.records.general.sex}}
- Species: {{:data.records.general.species}}
- Age: {{:data.records.general.age}}
- Rank: {{:data.records.general.rank}}
- Fingerprint: {{:data.records.general.fingerprint}}
- Physical Status: {{:data.records.general.p_stat}}
- Mental Status: {{:data.records.general.m_stat}}

- {{else}} - - General Record Lost!

-
- {{/if}} - {{if data.records.medical_exists == 1}} - Medical Data:
- Blood Type: {{:data.records.medical.b_type}}

- Minor Disabilities: {{:data.records.medical.mi_dis}}
- Details: {{:data.records.medical.mi_dis_d}}

- Major Disabilities: {{:data.records.medical.ma_dis}}
- Details: {{:data.records.medical.ma_dis_d}}

- Allergies: {{:data.records.medical.alg}}
- Details: {{:data.records.medical.alg_d}}

- Current Disease: {{:data.records.medical.cdi}}
- Details: {{:data.records.medical.alg_d}}

- Important Notes: {{:data.records.medical.notes}} - {{else}} - - Medical Record Lost! -
-
-
- {{/if}} -
-
-
- - - {{else data.mode == 45}} -

Security Record List

-
- Select A record -
-
- {{for data.records.security_records}} -
- {{:helper.link(value.Name, 'gear', {'cartmenu' : "1", 'choice' : "Security Records",'target' : value.ref}, null, null)}} -
- {{/for}} - - - {{else data.mode == 451}} -

Security Record

-
-
-
- {{if data.records.general_exists == 1}} - Name: {{:data.records.general.name}}
- Sex: {{:data.records.general.sex}}
- Species: {{:data.records.general.species}}
- Age: {{:data.records.general.age}}
- Rank: {{:data.records.general.rank}}
- Fingerprint: {{:data.records.general.fingerprint}}
- Physical Status: {{:data.records.general.p_stat}}
- Mental Status: {{:data.records.general.m_stat}}

- {{else}} - - General Record Lost!

-
- {{/if}} - {{if data.records.security_exists == 1}} - Security Data:
- Criminal Status: {{:data.records.security.criminal}}

- Minor Crimes: {{:data.records.security.mi_crim}}
- Details: {{:data.records.security.mi_crim_d}}

- Major Crimes: {{:data.records.security.ma_crim}}
- Details: {{:data.records.security.ma_crim_d}}

- Important Notes: {{:data.records.security.notes}} - {{else}} - - Security Record Lost!

-
- {{/if}} -
-
-
- - - {{else data.mode == 46}} -

Security Bot Control

- {{if data.records.beepsky.active == null || data.records.beepsky.active == 0}} - {{if data.records.beepsky.count == 0}} -

No bots found.

- {{else}} -
- Select A Bot. -
-
- {{for data.records.beepsky.bots}} -
- {{:helper.link(value.Name, 'gear', {'radiomenu' : "1", 'op' : "control",'bot' : value.ref}, null, null)}} (Location: {{:value.Location}}) -
- {{/for}} - {{/if}} -
- {{:helper.link('Scan for Bots','gear', {'radiomenu' : "1", 'op' : "scanbots"}, null, null)}} - {{else}} -

{{:data.records.beepsky.active}}

-

- {{if data.records.beepsky.botstatus.mode == -1}} -

Waiting for response...

- {{else}} -

Status:

-
-
-
- Location: -
-
- {{:data.records.beepsky.botstatus.loca}} -
-
-
-
- Mode: -
-
- - {{if data.records.beepsky.botstatus.mode ==0}} - Ready - {{else data.records.beepsky.botstatus.mode == 1}} - Apprehending target - {{else data.records.beepsky.botstatus.mode ==2 || data.records.beepsky.botstatus.mode == 3}} - Arresting target - {{else data.records.beepsky.botstatus.mode ==4}} - Starting patrol - {{else data.records.beepsky.botstatus.mode ==5}} - On Patrol - {{else data.records.beepsky.botstatus.mode ==6}} - Responding to summons - {{/if}} - -
-
-
- {{:helper.link('Stop Patrol', 'gear', {'radiomenu' : "1", 'op' : "stop"}, null, null)}} - {{:helper.link('Start Patrol', 'gear', {'radiomenu' : "1", 'op' : "go"}, null, null)}} - {{:helper.link('Summon Bot', 'gear', {'radiomenu' : "1", 'op' : "summon"}, null, null)}} -
- {{/if}} - {{:helper.link('Return to Bot list', 'gear', {'radiomenu' : "1", 'op' : "botlist"}, null, null)}} - {{/if}} - - - {{else data.mode == 47}} -

Supply Record Interlink

-
-
- Location: -
-
- - {{if data.records.supply.shuttle_moving}} - Moving to station ({{:data.records.supply.shuttle_eta}}) - {{else}} - Shuttle at {{:data.records.supply.shuttle_loc}} - {{/if}} - -
-
-
-
-
- Current Approved Orders
- {{if data.records.supply.approved_count == 0}} - No current approved orders

- {{else}} - {{for data.records.supply.approved}} - #{{:value.Number}} - {{:value.Name}} approved by {{:value.OrderedBy}}
{{if value.Comment != ""}} {{:value.Comment}}
{{/if}}
- {{/for}} - {{/if}} -

- Current Requested Orders
- {{if data.records.supply.requests_count == 0}} - No current requested orders

- {{else}} - {{for data.records.supply.requests}} - #{{:value.Number}} - {{:value.Name}} requested by {{:value.OrderedBy}}
{{if value.Comment != ""}} {{:value.Comment}}
{{/if}}
- {{/for}} - {{/if}} -
-
-
- - - {{else data.mode == 48}} -

Mule Control

- {{if data.records.mulebotcount == 0}} -

No bots found.

- {{else}} -

Mule List

- {{for data.records.mulebots}} -
-
Mulebot #{{:value.name}}
-
Location: {{:value.location}}
Home: {{:value.home}}
Target: {{:value.target}}
Load: {{:value.load}}
-
-
-
Status:
-
- {{if value.paused == 0}} - Nominal - {{else value.paused == 1}} - Paused - {{/if}} -
-
-
- {{:helper.link('Go home', null, {'cartmenu' : "1", 'choice' : "MULEbot", 'ref' : value.ref, 'command' : "Home"})}} - {{:helper.link('Set destination', null, {'cartmenu' : "1", 'choice' : "MULEbot", 'ref' : value.ref, 'command' : "SetD"})}} - {{:helper.link('Go', null, {'cartmenu' : "1", 'choice' : "MULEbot", 'ref' : value.ref, 'command' : "GoTD"})}} - {{:helper.link('Stop', null, {'cartmenu' : "1", 'choice' : "MULEbot", 'ref' : value.ref, 'command' : "Stop"})}} -
- {{/for}} - {{/if}} - - - {{else data.mode == 49}} -

Janatorial Supplies Locator

-
- Current Location: - {{if data.records.janitor.user_loc.x == 0}} - Unknown - {{else}} - {{:data.records.janitor.user_loc.x}} / {{:data.records.janitor.user_loc.y}} - {{/if}} -
-
- {{for data.records.janitor.mops}} - {{if value.x==0}} - Unable to locate Mop - {{else}} - Mop Location: - ({{:value.x}} / {{:value.y}}) - {{:value.dir}} - Status: {{:value.status}}
- {{/if}} - {{/for}} -
-
- {{for data.records.janitor.buckets}} - {{if value.x==0}} - Unable to locate Water Buckets - {{else}} - Water Buckets Location: - ({{:value.x}} / {{:value.y}}) - {{:value.dir}} - Water Level: {{:value.status}}
- {{/if}} - {{/for}} -
-
- {{for data.records.janitor.cleanbots}} - {{if value.x==0}} - Unable to locate Clean Bots - {{else}} - Clean Bots Location: - ({{:value.x}} / {{:value.y}}) - {{:value.dir}} - Status: {{:value.status}}
- {{/if}} - {{/for}} -
-
- {{for data.records.janitor.carts}} - {{if value.x==0}} - Unable to locate Janitorial Cart - {{else}} - Janitorial cart Location: - ({{:value.x}} / {{:value.y}}) - {{:value.dir}} - Status: {{:value.status}}
- {{/if}} - {{/for}} - - {{else data.mode == 6}} -

InstaNews ED 2.0.9

- -
-
- Functions: -
-
- {{:helper.link(data.news_silent==1 ? 'Ringer: Off' : 'Ringer: On', data.news_silent==1 ? 'volume-off' : 'volume-on', {'choice' : "Toggle News"}, null, 'fixedLeftWide')}} - {{:helper.link('Set news tone', 'comment', {'choice' : "Newstone"}, null, 'fixedLeftWide')}} -
-
- - {{if data.reception != 1}} - No reception with newscaster network. - {{/if}} - -
-
- {{for data.feedChannels}} - {{if value.censored}} - {{:helper.link(value.name, 'circle-arrow-s', {'choice' : "Select Feed", 'feed' : value.feed, 'name' : value.name } , null, 'fixedLeftWiderRed')}} - {{else}} - {{:helper.link(value.name, 'circle-arrow-s', {'choice' : "Select Feed", 'feed' : value.feed, 'name' : value.name } , null, 'fixedLeftWider')}} - {{/if}} - {{empty}} - No active channels found... - {{/for}} -
-
- - {{else data.mode == 61}} -

{{:data.feed.channel}}

- Created by: {{:data.feed.author}}
- - {{if data.reception != 1}} - No reception with newscaster network. - {{/if}} - -
-
-
- {{if data.feed.censored}} -

Attention

- This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.
- No further feed story additions are allowed while the D-Notice is in effect.
- {{else}} - {{for data.feed.messages}} - -{{:value.body}}
- {{if value.has_image}} -
- {{if value.caption}} - {{:value.caption}}
- {{/if}} - {{/if}} - [{{:value.message_type}} by {{:value.author}} - {{:value.time_stamp}}]
-
- {{empty}} - No feed messages found in channel... - {{/for}} - {{/if}} -
-
-
- - {{/if}} {{else}}







No Owner information found, please swipe ID
-{{/if}} - +{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_atmos_scan.tmpl b/nano/templates/pda_atmos_scan.tmpl new file mode 100644 index 0000000000..57f0a356a1 --- /dev/null +++ b/nano/templates/pda_atmos_scan.tmpl @@ -0,0 +1,60 @@ + +
+
+ {{if data.aircontents.reading == 1}} +
+ Pressure: +
+
+ {{:helper.string('{1} kPa', data.aircontents.pressure < 80 || data.aircontents.pressure > 120 ? 'bad' : data.aircontents.pressure < 95 || data.aircontents.pressure > 110 ? 'average' : 'good', helper.smoothRound(data.aircontents.pressure, 1))}} +
+
+ Temperature: +
+
+ {{:helper.string('{1} °C', data.aircontents.temp < 5 || data.aircontents.temp > 35 ? 'bad' : data.aircontents.temp < 15 || data.aircontents.temp > 25 ? 'average' : 'good' , helper.smoothRound(data.aircontents.temp, 1))}} +
+
+
+ Oxygen: +
+
+ {{:helper.string('{1}%', data.aircontents.oxygen < 17 ? 'bad' : data.aircontents.oxygen < 19 ? 'average' : 'good', helper.smoothRound(data.aircontents.oxygen, 1))}} +
+
+ Nitrogen: +
+
+ {{:helper.string('{1}%', data.aircontents.nitrogen > 82 ? 'bad' : data.aircontents.nitrogen > 80 ? 'average' : 'good', helper.smoothRound(data.aircontents.nitrogen, 1))}} +
+
+ Carbon Dioxide: +
+
+ {{:helper.string('{1}%', data.aircontents.carbon_dioxide > 5 ? 'bad' : 'good', helper.smoothRound(data.aircontents.carbon_dioxide, 1))}} +
+
+ Plasma: +
+
+ {{:helper.string('{1}%', data.aircontents.plasma > 0 ? 'bad' : 'good', helper.smoothRound(data.aircontents.plasma, 1))}} + +
+ {{if data.aircontents.other > 0}} +
+ Unknown: +
+
+ {{:helper.smoothRound(data.aircontents.other, 1)}}% +
+ {{/if}} + {{else}} +
+ Unable to get air reading +
+ {{/if}} +
+
\ No newline at end of file diff --git a/nano/templates/pda_janitor.tmpl b/nano/templates/pda_janitor.tmpl new file mode 100644 index 0000000000..864692d8a0 --- /dev/null +++ b/nano/templates/pda_janitor.tmpl @@ -0,0 +1,66 @@ +
+
+ Current Location: + {{if data.janitor.user_loc.x == 0}} + Unknown + {{else}} + {{:data.janitor.user_loc.x}} / {{:data.janitor.user_loc.y}} + {{/if}} +
+
+ {{if data.janitor.mops}} + Mop Locations: +
    + {{for data.janitor.mops}} +
  • + ({{:value.x}} / {{:value.y}}) - {{:value.dir}} - Status: {{:value.status}} +
  • + {{/for}} +
+ {{else}} + Unable to locate Mops + {{/if}} +
+
+ {{if data.janitor.buckets}} + Mop Bucket Locations: +
    + {{for data.janitor.buckets}} +
  • + ({{:value.x}} / {{:value.y}}) - {{:value.dir}} - Capacity: {{:value.volume}}/{{:value.max_volume}} +
  • + {{/for}} +
+ {{else}} + Unable to locate Mop Buckets + {{/if}} +
+
+ {{if data.janitor.cleanbots}} + Cleanbot Locations: +
    + {{for data.janitor.cleanbots}} +
  • + ({{:value.x}} / {{:value.y}}) - {{:value.dir}} - Status: {{:value.status}} +
  • + {{/for}} +
+ {{else}} + Unable to locate Cleanbots + {{/if}} +
+
+ {{if data.janitor.carts}} + Janitorial Cart Locations: +
    + {{for data.janitor.carts}} +
  • + ({{:value.x}} / {{:value.y}}) - {{:value.dir}} - Water Level: {{:value.volume}}/{{:value.max_volume}} +
  • + {{/for}} +
+ {{else}} + Unable to locate Janitorial Carts + {{/if}} +
+
diff --git a/nano/templates/pda_main_menu.tmpl b/nano/templates/pda_main_menu.tmpl new file mode 100644 index 0000000000..692cdb7039 --- /dev/null +++ b/nano/templates/pda_main_menu.tmpl @@ -0,0 +1,76 @@ + + + + +
+
+ Owner: +
+
+ {{:data.owner}}, {{:data.ownjob}} +
+
+
+
+ ID: +
+
+ {{:helper.link('Update PDA Info', 'refresh', {'choice' : "UpdateInfo"}, data.idInserted ? null : 'disabled', 'pdalink fixedLeftWide')}} +
+
+ +
+

Functions

+
+{{for data.categories : cat : i}} + +{{/for}} + +{{if data.pai}} +
+
+ PAI Utilities: +
+
+ {{:helper.link('Configuration', 'gear', {'choice' : "pai", 'option' : "1"}, null, 'pdalink fixedLeft')}} + {{:helper.link('Eject pAI', 'eject', {'choice' : "pai", 'option' : "2"}, null, 'pdalink fixedLeft')}} +
+
+{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_manifest.tmpl b/nano/templates/pda_manifest.tmpl new file mode 100644 index 0000000000..ef9e91a088 --- /dev/null +++ b/nano/templates/pda_manifest.tmpl @@ -0,0 +1,96 @@ + +
+
+ {{if data.manifest.heads}} + + {{for data.manifest["heads"]}} + {{if value.rank == "Captain"}} + + {{else}} + + {{/if}} + {{/for}} + {{/if}} + {{if data.manifest.sec}} + + {{for data.manifest["sec"]}} + {{if value.rank == "Head of Security"}} + + {{else}} + + {{/if}} + {{/for}} + {{/if}} + {{if data.manifest.eng}} + + {{for data.manifest["eng"]}} + {{if value.rank == "Chief Engineer"}} + + {{else}} + + {{/if}} + {{/for}} + {{/if}} + {{if data.manifest.med}} + + {{for data.manifest["med"]}} + {{if value.rank == "Chief Medical Officer"}} + + {{else}} + + {{/if}} + {{/for}} + {{/if}} + {{if data.manifest.sci}} + + {{for data.manifest["sci"]}} + {{if value.rank == "Research Director"}} + + {{else}} + + {{/if}} + {{/for}} + {{/if}} + {{if data.manifest.ser}} + + {{for data.manifest["ser"]}} + {{if value.rank == "Head of Personnel"}} + + {{else}} + + {{/if}} + {{/for}} + {{/if}} + {{if data.manifest.sup}} + + {{for data.manifest["sup"]}} + {{if value.rank == "Head of Personnel"}} + + {{else value.rank == "Quartermaster"}} + + {{else}} + + {{/if}} + {{/for}} + {{/if}} + {{if data.manifest.civ}} + + {{for data.manifest["civ"]}} + {{if value.rank == "Head of Personnel"}} + + {{else}} + + {{/if}} + {{/for}} + {{/if}} + {{if data.manifest.misc}} + + {{for data.manifest["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}}
Service
{{:value.name}}{{:value.rank}}{{:value.active}}
{{:value.name}}{{:value.rank}}{{:value.active}}
Supply
{{:value.name}}{{:value.rank}}{{:value.active}}
{{: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/pda_medical.tmpl b/nano/templates/pda_medical.tmpl new file mode 100644 index 0000000000..777fb91f4b --- /dev/null +++ b/nano/templates/pda_medical.tmpl @@ -0,0 +1,54 @@ +{{if !data.records}} +
+
Select a record:
+
+ {{for data.recordsList}} +
+ {{:helper.link(value.Name, 'user', {'choice' : "Records", 'target' : value.ref}, null, 'pdalink fixedLeftWidest')}} +
+ {{empty}} +
+ No records found. +
+ {{/for}} +{{else}} +
+
+
+ {{if data.records.general}} + Name: {{:data.records.general.name}}
+ Sex: {{:data.records.general.sex}}
+ Species: {{:data.records.general.species}}
+ Age: {{:data.records.general.age}}
+ Rank: {{:data.records.general.rank}}
+ Fingerprint: {{:data.records.general.fingerprint}}
+ Physical Status: {{:data.records.general.p_stat}}
+ Mental Status: {{:data.records.general.m_stat}}

+ {{else}} + + General Record Lost!

+
+ {{/if}} + {{if data.records.medical}} +
+
Medical Data:
+
+ Blood Type: {{:data.records.medical.b_type}}

+ Minor Disabilities: {{:data.records.medical.mi_dis}}
+ Details: {{:data.records.medical.mi_dis_d}}

+ Major Disabilities: {{:data.records.medical.ma_dis}}
+ Details: {{:data.records.medical.ma_dis_d}}

+ Allergies: {{:data.records.medical.alg}}
+ Details: {{:data.records.medical.alg_d}}

+ Current Disease: {{:data.records.medical.cdi}}
+ Details: {{:data.records.medical.alg_d}}

+ Important Notes: {{:data.records.medical.notes}} + {{else}} + + Medical Record Lost! + + {{/if}} +
+
+
+{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_messenger.tmpl b/nano/templates/pda_messenger.tmpl new file mode 100644 index 0000000000..4871934e4e --- /dev/null +++ b/nano/templates/pda_messenger.tmpl @@ -0,0 +1,90 @@ + +{{if data.active_conversation}} +
+
+ Messenger Functions: +
+
+ {{:helper.link('Delete Conversation', 'trash', {'choice' : "Clear", 'option' : "Convo"}, null, 'pdalink fixedLeftWide')}} + {{:helper.link(data.auto_scroll ? 'Autoscroll: On' : 'Autoscroll: Off', 'level-down', {'choice' : "Autoscroll"}, null, 'pdalink fixedLeftWide')}} +
+
+
+
+

Conversation with: {{:data.convo_name}} ({{:data.convo_job}})

+
+
+
+ {{for data.messages}} + {{if data.active_conversation == value.target}} + {{if value.sent==0}} + Them: {{:value.message}}
+ {{else}} + You: {{:value.message}}
+ {{/if}} + {{/if}} + {{/for}} +
+
+
+ {{:helper.link('Reply', 'comment', {'choice' : "Message", 'target': data.active_conversation}, null, 'pdalink fixedLeftWidest')}} + +{{else}} +
+
+ Messenger Functions: +
+
+ {{:helper.link(data.silent==1 ? 'Ringer: Off' : 'Ringer: On', data.silent==1 ? 'volume-off' : 'volume-up', {'choice' : "Toggle Ringer"}, null, 'pdalink fixedLeftWide')}} + {{:helper.link(data.toff==1 ? 'Messenger: Off' : 'Messenger: On',data.toff==1 ? 'close':'check', {'choice' : "Toggle Messenger"}, null, 'pdalink fixedLeftWide')}} + {{:helper.link('Set Ringtone', 'bell-o', {'choice' : "Ringtone"}, null, 'pdalink fixedLeftWide')}} + {{:helper.link('Delete all Conversations', 'trash', {'choice' : "Clear", 'option' : "All"}, null, 'pdalink fixedLeftWider')}} +
+
+ {{if data.toff == 0}} +
+ {{if data.charges}} +
+ {{:data.charges}} charges left. +

+
+ {{/if}} + + {{if !data.convopdas.length && !data.pdas.length}} + No other PDAS located + {{else}} +

Current Conversations

+ {{for data.convopdas}} +
+ {{:helper.link(value.Name, 'arrow-circle-down', {'choice' : "Select Conversation", 'convo' : value.Reference } , null, 'pdalink')}} + {{if data.charges}} + {{for data.plugins : plugin : i}} + {{:helper.link(plugin.name, plugin.icon, {'choice' : "Messenger Plugin", 'plugin' : plugin.ref, 'target' : value.Reference}, null, 'pdalink fixedLeft')}} + {{/for}} + {{/if}} +
+ {{/for}} +

Other PDAs

+ {{for data.pdas}} +
+ {{:helper.link(value.Name, 'arrow-circle-down', {'choice' : "Message", 'target' : value.Reference}, null, 'pdalink')}} + {{if data.charges}} + {{for data.plugins : plugin : i}} + {{:helper.link(plugin.name, plugin.icon, {'choice' : "Messenger Plugin", 'plugin' : plugin.ref, 'target' : value.Reference}, null, 'pdalink fixedLeft')}} + {{/for}} + {{/if}} +
+ {{/for}} + {{/if}} + {{/if}} +{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_mob_hunt.tmpl b/nano/templates/pda_mob_hunt.tmpl new file mode 100644 index 0000000000..dde4ab82a5 --- /dev/null +++ b/nano/templates/pda_mob_hunt.tmpl @@ -0,0 +1,91 @@ +
+
+ + + + {{if data.connected}} + + + {{else}} + + + {{/if}} + +
+ Connection Status: + + Connected + + {{:helper.link('Disconnect', 'sign-out', {'choice': 'Disconnect'})}} + + No Connection + + {{:helper.link('Connect', 'sign-in', {'choice': 'Reconnect'})}} +
+
+
+
+
My Collection:
+{{if data.no_collection}} +
+
+ Your collection is empty! Go capture some Nano-Mobs! +
+
+
+ Wild mobs captured: {{:data.wild_captures}} +{{else}} +
+
+
+ {{if data.entry}} + {{if data.entry.nickname}} + Name: {{:data.entry.nickname}}
+ {{/if}} + Species: {{:data.entry.real_name}}
+ Level: {{:data.entry.level}}
+ Primary Type: {{:data.entry.type1}}
+ {{if data.entry.type2}} + Secondary Type: {{:data.entry.type2}}
+ {{/if}} + {{if data.entry.sprite}} + + {{else}} + Mob Image Missing! + {{/if}} + {{else}} + + Mob Data Missing!

+
+ {{/if}} +
+
+
+
+ Wild mobs captured: {{:data.wild_captures}} +
+ + + + + + + + +
+ {{:helper.link('Previous Mob', 'arrow-left', {'choice': 'Prev'})}} + + {{:helper.link('Transfer Mob', 'exchange', {'choice': 'Transfer'})}} + + {{:helper.link('Rename Mob', 'pencil', {'choice': 'Rename'})}} + + {{:helper.link('Release Mob', 'tree', {'choice': 'Release'})}} + + {{:helper.link('Next Mob', 'arrow-right', {'choice': 'Next'})}} +
+ {{if data.entry.is_hacked}} +
+ {{:helper.link('Set Trap', 'bolt', {'choice': 'Set_Trap'})}} + {{/if}} +
+{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_mule.tmpl b/nano/templates/pda_mule.tmpl new file mode 100644 index 0000000000..369d0830aa --- /dev/null +++ b/nano/templates/pda_mule.tmpl @@ -0,0 +1,118 @@ +{{if !data.mulebot.active}} + {{if data.mulebot.count == 0}} +

No bots found.

+ {{else}} +
+ Select a MULE: +
+
+ {{for data.mulebot.bots}} +
+ {{:helper.link(value.Name, 'gear', {'radiomenu' : "1", 'op' : "control",'bot' : value.ref}, null, 'pdalink fixedLeftWidest')}} (Location: {{:value.Location}}) +
+ {{/for}} + {{/if}} +
+ {{:helper.link('Scan for Bots','rss', {'radiomenu' : "1", 'op' : "scanbots"}, null, 'pdalink fixedLeftWidest')}} +{{else}} +

{{:data.mulebot.active}}

+ {{if data.mulebot.botstatus.mode == -1}} +

Waiting for response...

+ {{else}} +

Status:

+
+
+ Location: +
+
+ {{:data.mulebot.botstatus.loca}} +
+
+
+
+ Mode: +
+
+ + {{if data.mulebot.botstatus.mode == 0}} + Ready + {{else data.mulebot.botstatus.mode == 1}} + Loading/Unloading + {{else data.mulebot.botstatus.mode == 2}} + Navigating to Delivery Location + {{else data.mulebot.botstatus.mode == 3}} + Navigating to Home + {{else data.mulebot.botstatus.mode == 4}} + Waiting for Clear Path + {{else data.mulebot.botstatus.mode == 5 || data.mulebot.botstatus.mode == 6}} + Calculating navigation Path + {{else data.mulebot.botstatus.mode == 7}} + Unable to locate destination + {{/if}} + +
+
+
+
+ Current Load: +
+
+ + {{:helper.link(data.mulebot.botstatus.load == null ? 'None (Unload)' : data.mulebot.botstatus.load + ' (Unload)', 'archive', {'radiomenu' : "1", 'op' : "unload"},data.mulebot.botstatus.load == null ? 'disabled' : null, 'pdalink fixedLeftWidest')}} + +
+
+
+
+ Power: +
+
+ + {{:data.mulebot.botstatus.powr}}% + +
+
+
+
+ Destination: +
+
+ {{:helper.link(data.mulebot.botstatus.dest == null || data.mulebot.botstatus.dest == "" ? 'None (Set)' : data.mulebot.botstatus.dest + ' (Set)', 'gear', {'radiomenu' : "1", 'op' : "setdest"}, null, 'pdalink fixedLeftWidest')}} +
+
+
+
+ Home: +
+
+ {{if data.mulebot.botstatus.home == null}} None {{else}} {{:data.mulebot.botstatus.home}} {{/if}} +
+
+
+
+ Auto Return: +
+
+ {{:helper.link(data.mulebot.botstatus.retn == 1 ? 'ON' : 'OFF', 'gear', {'radiomenu' : "1", 'op' : (data.mulebot.botstatus.retn==1 ? "retoff" : "reton")}, null, 'pdalink fixedLeftWidest')}} +
+
+
+
+ Auto Pickup: +
+
+ {{:helper.link(data.mulebot.botstatus.pick==1? 'ON' : 'OFF', 'gear', {'radiomenu' : "1", 'op' : (data.mulebot.botstatus.pick==1 ? "pickoff" : "pickon")}, null, 'pdalink fixedLeftWidest')}} +
+
+
+
+ Functions: +
+
+ {{:helper.link('Stop', 'gear', {'radiomenu' : "1", 'op' : "stop"}, null, 'pdalink fixedLeft')}} + {{:helper.link('Proceed', 'gear', {'radiomenu' : "1", 'op' : "start"}, null, 'pdalink fixedLeft')}} + {{:helper.link('Return Home', 'gear', {'radiomenu' : "1", 'op' : "home"}, null, 'pdalink fixedLeft')}} +
+
+ {{/if}} +{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_notekeeper.tmpl b/nano/templates/pda_notekeeper.tmpl new file mode 100644 index 0000000000..53db3be643 --- /dev/null +++ b/nano/templates/pda_notekeeper.tmpl @@ -0,0 +1,21 @@ + +
+
+ Notes: +
+
+
+
+
+ {{:data.note}} +
+
+
+
+
+ {{:helper.link('Edit Notes', 'pencil-square-o', {'choice': "Edit"}, null, 'pdalink')}} +
+
\ No newline at end of file diff --git a/nano/templates/pda_power.tmpl b/nano/templates/pda_power.tmpl new file mode 100644 index 0000000000..7ba04188b7 --- /dev/null +++ b/nano/templates/pda_power.tmpl @@ -0,0 +1,49 @@ +{{if !data.records.powerconnected}} +
+
Select a power monitor:
+
+ {{for data.records.powermonitors}} +
+ {{:helper.link(value.Name, 'exclamation-circle', {'choice' : "Power Select", 'target' : value.ref}, null, 'pdalink fixedLeftWidest')}} +
+ {{/for}} +{{else}} +
+
+ Total Power: +
+
+ {{:data.records.poweravail}} W +
+
+
+
+ Total Load: +
+
+ {{:data.records.powerload}} W +
+
+
+
+ Total Demand: +
+
+ {{:data.records.powerdemand}} W +
+
+
+ + + {{for data.records.apcs}} + + {{:helper.string('', value.Equipment == "On" || value.Equipment == "AOn" ? '#4f7529' : '#8f1414', value.Equipment)}} + {{:helper.string('', value.Lights == "On" || value.Lights == "AOn" ? '#4f7529' : '#8f1414', value.Lights)}} + {{:helper.string('', value.Environment == "On" || value.Environment == "AOn" ? '#4f7529' : '#8f1414', value.Environment)}} + {{:helper.string('', value.CellStatus == "F" ? '#4f7529' : value.CellStatus == "C" ? '#cd6500' : '#8f1414', value.CellStatus == "M" ? 'No Cell' : value.CellPct + '%', value.CellStatus == "M" ? '' : ' (' + value.CellStatus + ')')}} + + + {{/for}} +
AreaEquip.LightingEnviron.CellLoad
{{:value.Name}}{1}{1}{1}{1}{2}{{:value.Load}}W
+
+{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_secbot.tmpl b/nano/templates/pda_secbot.tmpl new file mode 100644 index 0000000000..6ff8c98e14 --- /dev/null +++ b/nano/templates/pda_secbot.tmpl @@ -0,0 +1,59 @@ +{{if !data.beepsky.active}} + {{if data.beepsky.count == 0}} +

No bots found.

+ {{else}} +
+ Select a bot: +
+
+ {{for data.beepsky.bots}} +
+ {{:helper.link(value.Name, 'gear', {'radiomenu' : "1", 'op' : "control",'bot' : value.ref}, null, 'pdalink fixedLeftWidest')}} (Location: {{:value.Location}}) +
+ {{/for}} + {{/if}} +
+ {{:helper.link('Scan for Bots','rss', {'radiomenu' : "1", 'op' : "scanbots"}, null, 'pdalink fixedLeftWidest')}} +{{else}} +

{{:data.beepsky.active}}

+ {{if data.beepsky.botstatus.mode == -1}} +

Waiting for response...

+ {{else}} +

Status:

+
+
+ Location: +
+
+ {{:data.beepsky.botstatus.loca}} +
+
+
+
+ Mode: +
+
+ + {{if data.beepsky.botstatus.mode ==0}} + Ready + {{else data.beepsky.botstatus.mode == 1}} + Apprehending target + {{else data.beepsky.botstatus.mode ==2 || data.beepsky.botstatus.mode == 3}} + Arresting target + {{else data.beepsky.botstatus.mode ==4}} + Starting patrol + {{else data.beepsky.botstatus.mode ==5}} + On Patrol + {{else data.beepsky.botstatus.mode ==6}} + Responding to summons + {{/if}} + +
+
+
+ {{:helper.link('Stop Patrol', 'gear', {'radiomenu' : "1", 'op' : "stop"}, null, 'pdalink fixedLeftWide')}} + {{:helper.link('Start Patrol', 'gear', {'radiomenu' : "1", 'op' : "go"}, null, 'pdalink fixedLeftWide')}} + {{:helper.link('Summon Bot', 'gear', {'radiomenu' : "1", 'op' : "summon"}, null, 'pdalink fixedLeftWide')}} +
+ {{/if}} +{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_security.tmpl b/nano/templates/pda_security.tmpl new file mode 100644 index 0000000000..53d40185e0 --- /dev/null +++ b/nano/templates/pda_security.tmpl @@ -0,0 +1,50 @@ +{{if !data.records}} +
+
Select a record:
+
+ {{for data.recordsList}} +
+ {{:helper.link(value.Name, 'user', {'choice' : "Records", 'target' : value.ref}, null, 'pdalink fixedLeftWidest')}} +
+ {{empty}} +
+ No records found. +
+ {{/for}} +{{else}} +
+
+
+ {{if data.records.general}} + Name: {{:data.records.general.name}}
+ Sex: {{:data.records.general.sex}}
+ Species: {{:data.records.general.species}}
+ Age: {{:data.records.general.age}}
+ Rank: {{:data.records.general.rank}}
+ Fingerprint: {{:data.records.general.fingerprint}}
+ Physical Status: {{:data.records.general.p_stat}}
+ Mental Status: {{:data.records.general.m_stat}}

+ {{else}} + + General Record Lost!

+
+ {{/if}} + {{if data.records.security}} +
+
Security Data:
+
+ Criminal Status: {{:data.records.security.criminal}}

+ Minor Crimes: {{:data.records.security.mi_crim}}
+ Details: {{:data.records.security.mi_crim_d}}

+ Major Crimes: {{:data.records.security.ma_crim}}
+ Details: {{:data.records.security.ma_crim_d}}

+ Important Notes: {{:data.records.security.notes}} + {{else}} + + Security Record Lost!

+
+ {{/if}} +
+
+
+{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_signaller.tmpl b/nano/templates/pda_signaller.tmpl new file mode 100644 index 0000000000..2b5f148b02 --- /dev/null +++ b/nano/templates/pda_signaller.tmpl @@ -0,0 +1,38 @@ + +
+
+ Frequency: +
+
+ {{:data.signal_freq}} +
+   + {{:helper.link('-1', null, {'choice' : "Signal Frequency", 'sfreq' : "-10"}, null, null)}}  + {{:helper.link('-.2', null, {'choice' : "Signal Frequency", 'sfreq' : "-2"}, null, null)}}  + + {{:helper.link('+.2', null, {'choice' : "Signal Frequency", 'sfreq' : "2"}, null, null)}}  + {{:helper.link('+1', null, {'choice' : "Signal Frequency", 'sfreq' : "10"}, null, null)}} +
+
+
+
+
+
+ Code: +
+
+ + {{:data.signal_code}}
+
+ {{:helper.link('-5', null, {'choice' : "Signal Code", 'scode' : "-5"}, null, null)}} + {{:helper.link('-1', null, {'choice' : "Signal Code", 'scode' : "-1"}, null, null)}} + {{:helper.link('+1', null, {'choice' : "Signal Code", 'scode' : "1"}, null, null)}} + {{:helper.link('+5', null, {'choice' : "Signal Code", 'scode' : "5"}, null, null)}} +
+
+
+ {{:helper.link('Send Signal', 'exclamation-circle', {'choice' : "Send Signal"}, null, null)}} +
\ No newline at end of file diff --git a/nano/templates/pda_status_display.tmpl b/nano/templates/pda_status_display.tmpl new file mode 100644 index 0000000000..8b153ad0ff --- /dev/null +++ b/nano/templates/pda_status_display.tmpl @@ -0,0 +1,44 @@ + +
+
+ Code: +
+
+ {{:helper.link('Clear', 'trash', {'choice' : "Status", 'statdisp' : "blank"}, null, 'pdalink fixedLeftWide')}} + {{:helper.link('Shuttle ETA', 'gear', {'cartmenu' : "1", 'choice' : "Status", 'statdisp' : "shuttle"}, null, 'pdalink fixedLeftWide')}} + {{:helper.link('Message', 'gear', {'choice' : "Status", 'statdisp' : "message"}, null, 'pdalink fixedLeftWide')}} +
+
+
+
+
+ Message line 1 +
+
+ {{:helper.link(data.records.message1 + ' (set)', 'pencil', {'choice' : "Status", 'statdisp' : "setmsg1"}, null, 'pdalink fixedLeftWide')}} +
+
+
+
+ Message line 2 +
+
+ {{:helper.link(data.records.message2 + ' (set)', 'pencil', {'choice' : "Status", 'statdisp' : "setmsg2"}, null, 'pdalink fixedLeftWide')}} +
+
+ +
+
+
+ ALERT!: +
+
+ {{:helper.link('None', 'bell', {'choice' : "Status", 'statdisp' : "alert", 'alert' : "default"}, null, 'pdalink fixedLeftWide')}} + {{:helper.link('Red Alert', 'bell', {'choice' : "Status", 'statdisp' : "alert", 'alert' : "redalert"}, null, 'pdalink fixedLeftWide')}} + {{:helper.link('Lockdown', 'exclamation-circle', {'choice' : "Status", 'statdisp' : "alert", 'alert' : "lockdown"}, null, 'pdalink fixedLeftWide')}} + {{:helper.link('Biohazard', 'exclamation-circle', {'choice' : "Status", 'statdisp' : "alert", 'alert' : "biohazard"}, null, 'pdalink fixedLeftWide')}} +
+
\ No newline at end of file diff --git a/nano/templates/pda_supply.tmpl b/nano/templates/pda_supply.tmpl new file mode 100644 index 0000000000..810fc19376 --- /dev/null +++ b/nano/templates/pda_supply.tmpl @@ -0,0 +1,39 @@ +
+
+ Location: +
+
+ + {{if data.supply.shuttle_moving}} + Moving to {{:data.supply.shuttle_loc}} + {{else}} + Shuttle at {{:data.supply.shuttle_loc}} + {{/if}} +
+ {{:data.supply.shuttle_time}} +
+
+
+
+
+
+ Current Approved Orders
+ {{if data.supply.approved_count == 0}} + No current approved orders

+ {{else}} + {{for data.supply.approved}} + #{{:value.Number}} - {{:value.Name}} approved by {{:value.OrderedBy}}
{{if value.Comment != ""}} {{:value.Comment}}
{{/if}}
+ {{/for}} + {{/if}} +

+ Current Requested Orders
+ {{if data.supply.requests_count == 0}} + No current requested orders

+ {{else}} + {{for data.supply.requests}} + #{{:value.Number}} - {{:value.Name}} requested by {{:value.OrderedBy}}
{{if value.Comment != ""}} {{:value.Comment}}
{{/if}}
+ {{/for}} + {{/if}} +
+
+
\ No newline at end of file diff --git a/tgui/packages/tgui/interfaces/Pda.js b/tgui/packages/tgui/interfaces/Pda.js new file mode 100644 index 0000000000..18018a1c73 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Pda.js @@ -0,0 +1,17 @@ +import { round } from 'common/math'; +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section } from "../components"; +import { Window } from "../layouts"; + +export const Pda = (props, context) => { + const { act, data } = useBackend(context); + + return ( + + + PDA! + + + ); +}; \ No newline at end of file diff --git a/tgui/packages/tgui/interfaces/pda/pda_main_menu.js b/tgui/packages/tgui/interfaces/pda/pda_main_menu.js new file mode 100644 index 0000000000..f580c273b6 --- /dev/null +++ b/tgui/packages/tgui/interfaces/pda/pda_main_menu.js @@ -0,0 +1,14 @@ +import { round } from 'common/math'; +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section } from "../components"; + +export const pda_main_menu = (props, context) => { + const { act, data } = useBackend(context); + + return ( +
+ Main Menu +
+ ); +}; \ No newline at end of file diff --git a/tools/nano-tester/index.html b/tools/nano-tester/index.html index 70f5dfecd2..f3cf4ba855 100644 --- a/tools/nano-tester/index.html +++ b/tools/nano-tester/index.html @@ -25,6 +25,7 @@ + diff --git a/vorestation.dme b/vorestation.dme index 16816e2506..f87cd47756 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -65,6 +65,7 @@ #include "code\__defines\nifsoft.dm" #include "code\__defines\objects.dm" #include "code\__defines\overmap.dm" +#include "code\__defines\pda.dm" #include "code\__defines\planets.dm" #include "code\__defines\plants.dm" #include "code\__defines\preferences.dm" @@ -3067,11 +3068,17 @@ #include "code\modules\paperwork\stamps.dm" #include "code\modules\pda\app.dm" #include "code\modules\pda\cart.dm" +#include "code\modules\pda\cart_apps.dm" #include "code\modules\pda\cart_vr.dm" #include "code\modules\pda\chatroom.dm" +#include "code\modules\pda\core_apps.dm" +#include "code\modules\pda\messenger.dm" +#include "code\modules\pda\messenger_plugins.dm" #include "code\modules\pda\pda.dm" +#include "code\modules\pda\pda_tgui.dm" #include "code\modules\pda\pda_vr.dm" #include "code\modules\pda\radio.dm" +#include "code\modules\pda\utilities.dm" #include "code\modules\persistence\filth.dm" #include "code\modules\persistence\graffiti.dm" #include "code\modules\persistence\noticeboard.dm"