//The advanced pea-green monochrome lcd of tomorrow. GLOBAL_LIST_EMPTY(PDAs) #define PDA_SCANNER_NONE 0 #define PDA_SCANNER_MEDICAL 1 #define PDA_SCANNER_FORENSICS 2 //unused #define PDA_SCANNER_REAGENT 3 #define PDA_SCANNER_HALOGEN 4 #define PDA_SCANNER_GAS 5 #define PDA_SPAM_DELAY 2 MINUTES /obj/item/pda name = "\improper standard PDA" desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge." icon = 'icons/obj/pda.dmi' icon_state = "pda" inhand_icon_state = "electronic" worn_icon_state = "pda" lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' greyscale_config = /datum/greyscale_config/pda greyscale_colors = "#999875#a92323" item_flags = NOBLUDGEON w_class = WEIGHT_CLASS_TINY slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT actions_types = list(/datum/action/item_action/toggle_light) armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 100, ACID = 100) resistance_flags = FIRE_PROOF | ACID_PROOF light_system = MOVABLE_LIGHT_DIRECTIONAL light_range = 2.3 light_power = 0.6 light_color = "#FFCC66" light_on = FALSE custom_materials = list(/datum/material/iron=300, /datum/material/glass=100, /datum/material/plastic=100) //Main variables var/owner = null // String name of owner var/default_cartridge = 0 // Access level defined by cartridge var/obj/item/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/icon_alert = "pda-r" //Icon to be overlayed for message alerts. Taken from the pda icon file. var/font_index = 0 //This int tells DM which font is currently selected and lets DM know when the last font has been selected so that it can cycle back to the first font when "toggle font" is pressed again. var/font_mode = "font-family:monospace;" //The currently selected font. var/background_color = "#808000" //The currently selected background color. #define FONT_MONO "font-family:monospace;" #define FONT_SHARE "font-family:\"Share Tech Mono\", monospace;letter-spacing:0px;" #define FONT_ORBITRON "font-family:\"Orbitron\", monospace;letter-spacing:0px; font-size:15px" #define FONT_VT "font-family:\"VT323\", monospace;letter-spacing:1px;" #define MODE_MONO 0 #define MODE_SHARE 1 #define MODE_ORBITRON 2 #define MODE_VT 3 //Secondary variables var/scanmode = PDA_SCANNER_NONE var/silent = FALSE //To beep or not to beep, that is the question var/toff = FALSE //If TRUE, messenger disabled var/tnote = null //Current Texts var/last_text //No text spamming var/last_everyone //No text for everyone spamming var/last_noise //Also no honk spamming that's bad too var/ttone = "beep" //The ringtone! var/honkamt = 0 //How many honks left when infected with honk.exe var/mimeamt = 0 //How many silence left when infected with mime.exe var/note = "Congratulations, your station has chosen the Thinktronic 5230 Personal Data Assistant!" //Current note in the notepad function var/notehtml = "" var/notescanned = FALSE // True if what is in the notekeeper was from a paper. var/hidden = FALSE // Is the PDA hidden from the PDA list? var/emped = FALSE var/equipped = FALSE //used here to determine if this is the first time its been picked up var/allow_emojis = FALSE //if the pda can send emojis and actually have them parsed as such var/sort_by_job = FALSE // If this is TRUE, will sort PDA list by job. var/obj/item/card/id/id = null //Making it possible to slot an ID card into the PDA so it can function as both. var/ownjob = null //related to above var/obj/item/paicard/pai = null // A slot for a personal AI device var/datum/picture/picture //Scanned photo var/list/contained_item = list(/obj/item/pen, /obj/item/toy/crayon, /obj/item/lipstick, /obj/item/flashlight/pen, /obj/item/clothing/mask/cigarette) var/obj/item/inserted_item //Used for pen, crayon, and lipstick insertion or removal. Same as above. var/overlays_x_offset = 0 //x offset to use for certain overlays var/underline_flag = TRUE //flag for underline /obj/item/pda/suicide_act(mob/living/carbon/user) var/deathMessage = msg_input(user) if (!deathMessage) deathMessage = "i ded" user.visible_message("[user] is sending a message to the Grim Reaper! It looks like [user.p_theyre()] trying to commit suicide!") tnote += "→ To The Grim Reaper:
[deathMessage]
"//records a message in their PDA as being sent to the grim reaper return BRUTELOSS /obj/item/pda/examine(mob/user) . = ..() if(!id && !inserted_item) return if(id) . += "Alt-click to remove the ID." //won't name ID on examine in case it's stolen if(inserted_item && (!isturf(loc))) . += "Ctrl-click to remove [inserted_item]." //traitor pens are disguised so we're fine naming them on examine if((!isnull(cartridge))) . += "Ctrl+Shift-click to remove the cartridge." //won't name cart on examine in case it's Detomatix /obj/item/pda/Initialize() . = ..() GLOB.PDAs += src if(default_cartridge) cartridge = new default_cartridge(src) if(inserted_item) inserted_item = new inserted_item(src) else inserted_item = new /obj/item/pen(src) RegisterSignal(src, COMSIG_LIGHT_EATER_ACT, .proc/on_light_eater) update_appearance() /obj/item/pda/equipped(mob/user, slot) . = ..() if(!equipped) if(user.client) background_color = user.client.prefs.pda_color switch(user.client.prefs.pda_style) if(MONO) font_index = MODE_MONO font_mode = FONT_MONO if(SHARE) font_index = MODE_SHARE font_mode = FONT_SHARE if(ORBITRON) font_index = MODE_ORBITRON font_mode = FONT_ORBITRON if(VT) font_index = MODE_VT font_mode = FONT_VT else font_index = MODE_MONO font_mode = FONT_MONO equipped = TRUE /obj/item/pda/proc/update_label() name = "PDA-[owner] ([ownjob])" //Name generalisation /obj/item/pda/GetAccess() if(id) return id.GetAccess() else return ..() /obj/item/pda/get_id_examine_strings(mob/user) . = ..() if(id) . += "\The [src] is displaying [id]." . += id.get_id_examine_strings(user) /obj/item/pda/GetID() return id /obj/item/pda/RemoveID() return do_remove_id() /obj/item/pda/InsertID(obj/item/inserting_item) var/obj/item/card/inserting_id = inserting_item.RemoveID() if(!inserting_id) return insert_id(inserting_id) if(id == inserting_id) return TRUE return FALSE /obj/item/pda/update_overlays() . = ..() if(!initial(icon)) return var/mutable_appearance/overlay = new(initial(icon)) overlay.pixel_x = overlays_x_offset if(id) overlay.icon_state = "id_overlay" . += new /mutable_appearance(overlay) if(inserted_item) overlay.icon_state = "insert_overlay" . += new /mutable_appearance(overlay) if(light_on) overlay.icon_state = "light_overlay" . += new /mutable_appearance(overlay) if(pai) if(pai.pai) overlay.icon_state = "pai_overlay" . += new /mutable_appearance(overlay) else overlay.icon_state = "pai_off_overlay" . += new /mutable_appearance(overlay) /obj/item/pda/MouseDrop(mob/over, src_location, over_location) var/mob/M = usr if((M == over) && usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) return attack_self(M) return ..() /obj/item/pda/attack_self_tk(mob/user) to_chat(user, "The PDA's capacitive touch screen doesn't seem to respond!") return COMPONENT_CANCEL_ATTACK_CHAIN /obj/item/pda/interact(mob/user) if(!ISADVANCEDTOOLUSER(user)) to_chat(user, "You don't have the dexterity to do this!") return ..() var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/simple/pda) assets.send(user) var/datum/asset/spritesheet/emoji_s = get_asset_datum(/datum/asset/spritesheet/chat) emoji_s.send(user) //Already sent by chat but no harm doing this user.set_machine(src) var/dat = "Personal Data Assistant" dat += assets.css_tag() dat += emoji_s.css_tag() dat += "[PDAIMG(refresh)]Refresh" if ((!isnull(cartridge)) && (mode == 0)) dat += " | [PDAIMG(eject)]Eject [cartridge]" if (mode) dat += " | [PDAIMG(menu)]Return" if (mode == 0) dat += "
" dat += "
Toggle Font" dat += " | Change Color" dat += " | Toggle Underline" //underline button dat += "
" dat += "
" if (!owner) dat += "Warning: No owner information entered. Please swipe card.

" dat += "[PDAIMG(refresh)]Retry" else switch (mode) if (0) dat += "

PERSONAL DATA ASSISTANT v.1.2

" dat += "Owner: [owner], [ownjob]
" dat += text("ID: [id ? "[id.registered_name], [id.assignment]" : "----------"]") dat += text("
[id ? "Update PDA Info" : ""]

") dat += "[station_time_timestamp()]
" //:[world.time / 100 % 6][world.time / 100 % 10]" dat += "[time2text(world.realtime, "MMM DD")] [GLOB.year_integer+540]" dat += "

" dat += "

General Functions

" dat += "" if (cartridge.access & CART_ENGINE) dat += "

Engineering Functions

" dat += "" if (cartridge.access & CART_MEDICAL) dat += "

Medical Functions

" dat += "" if (cartridge.access & CART_SECURITY) dat += "

Security Functions

" dat += "" if(cartridge.access & CART_QUARTERMASTER) dat += "

Quartermaster Functions:

" dat += "" dat += "" dat += "

Utilities

" dat += "" if (1) dat += "

[PDAIMG(notes)] Notekeeper V2.2

" dat += "Edit
" if(notescanned) dat += "(This is a scanned image, editing it may cause some text formatting to change.)
" dat += "
[(!notehtml ? note : notehtml)]" if (2) dat += "

[PDAIMG(mail)] SpaceMessenger V3.9.6

" dat += "[PDAIMG(bell)]Ringer: [silent == 1 ? "Off" : "On"] | " dat += "[PDAIMG(mail)]Send / Receive: [toff == 1 ? "Off" : "On"] | " dat += "[PDAIMG(bell)]Set Ringtone | " dat += "[PDAIMG(mail)]Messages
" dat += "Sorted by: [sort_by_job ? "Job" : "Name"]" if(cartridge) dat += cartridge.message_header() dat += "

[PDAIMG(menu)] Detected PDAs

" dat += "" if (count == 0) dat += "None detected.
" else if(cartridge?.spam_enabled) dat += "Send To All" if(6) dat += "

[PDAIMG(mail)] ExperTrak® Skill Tracker V4.26.2

" dat += "Thank you for choosing ExperTrak® brand software! ExperTrak® inc. is proud to be a Nanotrasen employee expertise and effectiveness department subsidary!" dat += "

This software is designed to track and monitor your skill development as a Nanotrasen employee. Your job performance across different fields has been quantified and categorized below.
" var/datum/mind/targetmind = user.mind if(targetmind) for (var/type in GLOB.skill_types) var/datum/skill/S = GetSkillRef(type) var/lvl_num = targetmind.get_skill_level(type) var/lvl_name = uppertext(targetmind.get_skill_level_name(type)) var/exp = targetmind.get_skill_exp(type) var/xp_prog_to_level = targetmind.exp_needed_to_level_up(type) var/xp_req_to_level = 0 if (xp_prog_to_level)//is it even possible to level up? xp_req_to_level = SKILL_EXP_LIST[lvl_num+1] - SKILL_EXP_LIST[lvl_num] dat += "
[S.name]" dat += "
[S.desc]" dat += "" if(21) if(icon_alert && !istext(icon_alert)) cut_overlay(icon_alert) icon_alert = initial(icon_alert) dat += "

[PDAIMG(mail)] SpaceMessenger V3.9.6

" dat += "[PDAIMG(blank)]Clear Messages" dat += "

[PDAIMG(mail)] Messages

" dat += tnote dat += "
" if (3) dat += "

[PDAIMG(atmos)] Atmospheric Readings

" var/turf/T = user.loc if (isnull(T)) dat += "Unable to obtain a reading.
" else var/datum/gas_mixture/environment = T.return_air() var/list/env_gases = environment.gases var/pressure = environment.return_pressure() var/total_moles = environment.total_moles() dat += "Air Pressure: [round(pressure,0.1)] kPa
" if (total_moles) for(var/id in env_gases) var/gas_level = env_gases[id][MOLES]/total_moles if(gas_level > 0) dat += "[env_gases[id][GAS_META][META_GAS_NAME]]: [round(gas_level*100, 0.01)]%
" dat += "Temperature: [round(environment.temperature-T0C)]°C
" dat += "
" else//Else it links to the cart menu proc. Although, it really uses menu hub 4--menu 4 doesn't really exist as it simply redirects to hub. dat += cartridge.generate_menu() dat += "" if (underline_flag) dat = replacetext(dat, "text-decoration:none", "text-decoration:underline") if (!underline_flag) dat = replacetext(dat, "text-decoration:underline", "text-decoration:none") user << browse(dat, "window=pda;size=400x450;border=1;can_resize=1;can_minimize=0") onclose(user, "pda", src) /obj/item/pda/Topic(href, href_list) ..() var/mob/living/U = usr //Looking for master was kind of pointless since PDAs don't appear to have one. if(usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) && !href_list["close"]) add_fingerprint(U) U.set_machine(src) switch(href_list["choice"]) //BASIC FUNCTIONS=================================== if("Refresh")//Refresh, goes to the end of the proc. if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) if ("Toggle_Font") //CODE REVISION 2 font_index = (font_index + 1) % 4 switch(font_index) if (MODE_MONO) font_mode = FONT_MONO if (MODE_SHARE) font_mode = FONT_SHARE if (MODE_ORBITRON) font_mode = FONT_ORBITRON if (MODE_VT) font_mode = FONT_VT if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) if ("Change_Color") var/new_color = input("Please enter a color name or hex value (Default is \'#808000\').",background_color)as color background_color = new_color if ("Toggle_Underline") underline_flag = !underline_flag if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) if("Return")//Return if(mode<=9) mode = 0 else mode = round(mode/10) if(mode==4 || mode == 5)//Fix for cartridges. Redirects to hub. mode = 0 if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) if ("Authenticate")//Checks for ID id_check(U) if("UpdateInfo") ownjob = id.assignment update_label() if(!silent) playsound(src, 'sound/machines/terminal_processing.ogg', 15, TRUE) addtimer(CALLBACK(GLOBAL_PROC, .proc/playsound, src, 'sound/machines/terminal_success.ogg', 15, TRUE), 1.3 SECONDS) if("Eject")//Ejects the cart, only done from hub. eject_cart(U) if(!silent) playsound(src, 'sound/machines/terminal_eject.ogg', 50, TRUE) //MENU FUNCTIONS=================================== if("0")//Hub mode = 0 if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) if("1")//Notes mode = 1 if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) if("2")//Messenger mode = 2 if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) if("21")//Read messeges mode = 21 if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) if("3")//Atmos scan mode = 3 if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) if("4")//Redirects to hub mode = 0 if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) //MAIN FUNCTIONS=================================== if("Light") toggle_light(U) if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) if("Medical Scan") if(scanmode == PDA_SCANNER_MEDICAL) scanmode = PDA_SCANNER_NONE else if((!isnull(cartridge)) && (cartridge.access & CART_MEDICAL)) scanmode = PDA_SCANNER_MEDICAL if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) if("Reagent Scan") if(scanmode == PDA_SCANNER_REAGENT) scanmode = PDA_SCANNER_NONE else if((!isnull(cartridge)) && (cartridge.access & CART_REAGENT_SCANNER)) scanmode = PDA_SCANNER_REAGENT if("Halogen Counter") if(scanmode == PDA_SCANNER_HALOGEN) scanmode = PDA_SCANNER_NONE else if((!isnull(cartridge)) && (cartridge.access & CART_ENGINE)) scanmode = PDA_SCANNER_HALOGEN if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) if("Honk") if ( !(last_noise && world.time < last_noise + 20) ) playsound(src, 'sound/items/bikehorn.ogg', 50, TRUE) last_noise = world.time if("Trombone") if ( !(last_noise && world.time < last_noise + 20) ) playsound(src, 'sound/misc/sadtrombone.ogg', 50, TRUE) last_noise = world.time if("Gas Scan") if(scanmode == PDA_SCANNER_GAS) scanmode = PDA_SCANNER_NONE else if((!isnull(cartridge)) && (cartridge.access & CART_ATMOS)) scanmode = PDA_SCANNER_GAS if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) if("Drone Phone") var/alert_s = input(U,"Alert severity level","Ping Drones",null) as null|anything in list("Low","Medium","High","Critical") var/area/A = get_area(U) if(A && alert_s && !QDELETED(U)) var/msg = "NON-DRONE PING: [U.name]: [alert_s] priority alert in [A.name]!" _alert_drones(msg, TRUE, U) to_chat(U, msg) if(!silent) playsound(src, 'sound/machines/terminal_success.ogg', 15, TRUE) if("Drone Access") var/mob/living/simple_animal/drone/drone_user = U if(isdrone(U) && drone_user.shy) to_chat(U, "Your laws prevent this action.") return var/new_state = text2num(href_list["drone_blacklist"]) GLOB.drone_machine_blacklist_enabled = new_state if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) //NOTEKEEPER FUNCTIONS=================================== if ("Edit") var/n = stripped_multiline_input(U, "Please enter message", name, note) if (in_range(src, U) && loc == U) if (mode == 1 && n) note = n notehtml = parsemarkdown(n, U) notescanned = FALSE else U << browse(null, "window=pda") return //MESSENGER FUNCTIONS=================================== if("Toggle Messenger") toff = !toff if("Toggle Ringer")//If viewing texts then erase them, if not then toggle silent status silent = !silent if("Clear")//Clears messages tnote = null if("Ringtone") var/t = stripped_input(U, "Please enter new ringtone", name, ttone, 20) if(in_range(src, U) && loc == U && t) if(SEND_SIGNAL(src, COMSIG_PDA_CHANGE_RINGTONE, U, t) & COMPONENT_STOP_RINGTONE_CHANGE) U << browse(null, "window=pda") return else ttone = t else U << browse(null, "window=pda") return if("Message") create_message(U, locate(href_list["target"]) in GLOB.PDAs) if("Sorting Mode") sort_by_job = !sort_by_job if("MessageAll") if(cartridge?.spam_enabled) send_to_all(U) if("cart") if(cartridge) cartridge.special(U, href_list) else U << browse(null, "window=pda") return //SYNDICATE FUNCTIONS=================================== if("Toggle Door") if(cartridge && cartridge.access & CART_REMOTE_DOOR) for(var/obj/machinery/door/poddoor/M in GLOB.machines) if(M.id == cartridge.remote_door_id) if(M.density) M.open() else M.close() //pAI FUNCTIONS=================================== if("pai") switch(href_list["option"]) if("1") // Configure pAI device pai.attack_self(U) if("2") // Eject pAI device usr.put_in_hands(pai) to_chat(usr, "You remove the pAI from the [name].") //SKILL FUNCTIONS=================================== if("SkillReward") var/type = text2path(href_list["skill"]) var/datum/skill/S = GetSkillRef(type) var/datum/mind/mind = U.mind var/new_level = mind.get_skill_level(type) S.try_skill_reward(mind, new_level) //LINK FUNCTIONS=================================== else//Cartridge menu linking mode = max(text2num(href_list["choice"]), 0) else//If not in range, can't interact or not using the pda. U.unset_machine() U << browse(null, "window=pda") return //EXTRA FUNCTIONS=================================== if (mode == 2 || mode == 21)//To clear message overlays. update_appearance() if ((honkamt > 0) && (prob(60)))//For clown virus. honkamt-- playsound(src, 'sound/items/bikehorn.ogg', 30, TRUE) if(U.machine == src && href_list["skiprefresh"]!="1")//Final safety. attack_self(U)//It auto-closes the menu prior if the user is not in range and so on. else U.unset_machine() U << browse(null, "window=pda") return /obj/item/pda/proc/remove_id(mob/user) if(issilicon(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) return do_remove_id(user) /obj/item/pda/proc/do_remove_id(mob/user) if(!id) return if(user) user.put_in_hands(id) to_chat(user, "You remove the ID from the [name].") else id.forceMove(get_turf(src)) . = id id = null updateSelfDialog() update_appearance() playsound(src, 'sound/machines/terminal_eject.ogg', 50, TRUE) if(ishuman(loc)) var/mob/living/carbon/human/H = loc if(H.wear_id == src) H.sec_hud_set_ID() update_slot_icon() /obj/item/pda/proc/msg_input(mob/living/U = usr) var/t = stripped_input(U, "Please enter message", name) if (!t || toff) return if(!U.canUseTopic(src, BE_CLOSE)) return if(emped) t = Gibberish(t, TRUE) return t /obj/item/pda/proc/send_message(mob/living/user, list/obj/item/pda/targets, everyone) var/message = msg_input(user) if(!message || !targets.len) return if((last_text && world.time < last_text + 10) || (everyone && last_everyone && world.time < last_everyone + PDA_SPAM_DELAY)) return if(prob(1)) message += "\nSent from my PDA" // Send the signal var/list/string_targets = list() for (var/obj/item/pda/P in targets) if (P.owner && P.ownjob) // != src is checked by the UI string_targets += "[P.owner] ([P.ownjob])" for (var/obj/machinery/computer/message_monitor/M in targets) // In case of "Reply" to a message from a console, this will make the // message be logged successfully. If the console is impersonating // someone by matching their name and job, the reply will reach the // impersonated PDA. string_targets += "[M.customsender] ([M.customjob])" if (!string_targets.len) return var/datum/signal/subspace/messaging/pda/signal = new(src, list( "name" = "[owner]", "job" = "[ownjob]", "message" = message, "targets" = string_targets, "emojis" = allow_emojis, )) if (picture) signal.data["photo"] = picture signal.send_to_receivers() // If it didn't reach, note that fact if (!signal.data["done"]) to_chat(user, "ERROR: Server isn't responding.") if(!silent) playsound(src, 'sound/machines/terminal_error.ogg', 15, TRUE) return var/target_text = signal.format_target() if(allow_emojis) message = emoji_parse(message)//already sent- this just shows the sent emoji as one to the sender in the to_chat signal.data["message"] = emoji_parse(signal.data["message"]) // Log it in our logs tnote += "→ To [target_text]:
[signal.format_message()]
" // Show it to ghosts var/ghost_message = "[owner] PDA Message --> [target_text]: [signal.format_message()]" for(var/mob/M in GLOB.player_list) if(isobserver(M) && (M.client.prefs.chat_toggles & CHAT_GHOSTPDA)) to_chat(M, "[FOLLOW_LINK(M, user)] [ghost_message]") // Log in the talk log user.log_talk(message, LOG_PDA, tag="PDA: [initial(name)] to [target_text]") to_chat(user, "PDA message sent to [target_text]: \"[message]\"") if(!silent) playsound(src, 'sound/machines/terminal_success.ogg', 15, TRUE) // Reset the photo picture = null last_text = world.time if (everyone) last_everyone = world.time /obj/item/pda/proc/receive_message(datum/signal/subspace/messaging/pda/signal) tnote += "← From [signal.data["name"]] ([signal.data["job"]]):
[signal.format_message()]
" if (!silent) if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED)) playsound(src, pick('sound/machines/twobeep_voice1.ogg', 'sound/machines/twobeep_voice2.ogg'), 50, TRUE) else playsound(src, 'sound/machines/twobeep_high.ogg', 50, TRUE) audible_message("[icon2html(src, hearers(src))] *[ttone]*", null, 3) //Search for holder of the PDA. var/mob/living/L = null if(loc && isliving(loc)) L = loc //Maybe they are a pAI! else L = get(src, /mob/living/silicon) if(L && (L.stat == CONSCIOUS || L.stat == SOFT_CRIT)) var/reply = "(Reply)" var/hrefstart var/hrefend if (isAI(L)) hrefstart = "" hrefend = "" if(signal.data["automated"]) reply = "\[Automated Message\]" var/inbound_message = signal.format_message() if(signal.data["emojis"] == TRUE)//so will not parse emojis as such from pdas that don't send emojis inbound_message = emoji_parse(inbound_message) to_chat(L, "[icon2html(src)] PDA message from [hrefstart][signal.data["name"]] ([signal.data["job"]])[hrefend], [inbound_message] [reply]") update_appearance() if(istext(icon_alert)) icon_alert = mutable_appearance(initial(icon), icon_alert) add_overlay(icon_alert) /obj/item/pda/proc/send_to_all(mob/living/U) if (last_everyone && world.time < last_everyone + PDA_SPAM_DELAY) to_chat(U,"Send To All function is still on cooldown.") return send_message(U,get_viewable_pdas(), TRUE) /obj/item/pda/proc/create_message(mob/living/U, obj/item/pda/P) send_message(U,list(P)) /obj/item/pda/AltClick(mob/user) ..() if(id) remove_id(user) else remove_pen(user) /obj/item/pda/CtrlClick(mob/user) ..() if(isturf(loc)) //stops the user from dragging the PDA by ctrl-clicking it. return remove_pen(user) /obj/item/pda/CtrlShiftClick(mob/user) ..() eject_cart(user) /obj/item/pda/verb/verb_toggle_light() set name = "Toggle light" set category = "Object" set src in oview(1) toggle_light(usr) /obj/item/pda/verb/verb_remove_id() set category = "Object" set name = "Eject ID" set src in usr if(id) remove_id(usr) else to_chat(usr, "This PDA does not have an ID in it!") /obj/item/pda/verb/verb_remove_pen() set category = "Object" set name = "Remove Pen" set src in usr remove_pen(usr) /obj/item/pda/verb/verb_eject_cart() set category = "Object" set name = "Eject Cartridge" set src in usr eject_cart(usr) /obj/item/pda/proc/toggle_light(mob/user) if(issilicon(user) || !user.canUseTopic(src, BE_CLOSE)) return if(light_on) set_light_on(FALSE) else if(light_range) set_light_on(TRUE) update_appearance() for(var/X in actions) var/datum/action/A = X A.UpdateButtonIcon() /// Special light eater handling /obj/item/pda/proc/on_light_eater(obj/item/pda/source, datum/light_eater) SIGNAL_HANDLER set_light_on(FALSE) set_light_range(0) //We won't be turning on again. update_appearance() visible_message("The light in [src] shorts out!") return COMPONENT_BLOCK_LIGHT_EATER /obj/item/pda/proc/remove_pen(mob/user) if(issilicon(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) //TK doesn't work even with this removed but here for readability return if(inserted_item) user.put_in_hands(inserted_item) to_chat(user, "You remove [inserted_item] from [src].") inserted_item = null update_appearance() playsound(src, 'sound/machines/pda_button2.ogg', 50, TRUE) else to_chat(user, "This PDA does not have a pen in it!") /obj/item/pda/proc/eject_cart(mob/user) if(issilicon(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) //TK disabled to stop cartridge teleporting into hand return if (!isnull(cartridge)) user.put_in_hands(cartridge) to_chat(user, "You eject [cartridge] from [src].") scanmode = PDA_SCANNER_NONE cartridge.host_pda = null cartridge = null updateSelfDialog() update_appearance() //trying to insert or remove an id /obj/item/pda/proc/id_check(mob/user, obj/item/card/id/I) if(!I) if(id && (src in user.contents)) remove_id(user) return TRUE else var/obj/item/card/id/C = user.get_active_held_item() if(istype(C)) I = C if(I?.registered_name) if(!user.transferItemToLoc(I, src)) return FALSE insert_id(I, user) update_appearance() playsound(src, 'sound/machines/pda_button1.ogg', 50, TRUE) return TRUE /obj/item/pda/proc/insert_id(obj/item/card/id/inserting_id, mob/user) var/obj/old_id = id id = inserting_id if(ishuman(loc)) var/mob/living/carbon/human/human_wearer = loc if(human_wearer.wear_id == src) human_wearer.sec_hud_set_ID() if(old_id) if(user) user.put_in_hands(old_id) else old_id.forceMove(get_turf(src)) update_slot_icon() /obj/item/pda/pre_attack(obj/target, mob/living/user, params) if(!ismachinery(target)) return ..() var/obj/machinery/target_machine = target if(!target_machine.panel_open && !istype(target, /obj/machinery/computer)) return ..() if(!istype(cartridge, /obj/item/cartridge/virus/clown)) return ..() var/obj/item/cartridge/virus/installed_cartridge = cartridge if(installed_cartridge.charges <=0) to_chat(user, "Out of charges.") return ..() to_chat(user, "You upload the virus to the airlock controller!") var/sig_list if(istype(target,/obj/machinery/door/airlock)) sig_list += list(COMSIG_AIRLOCK_OPEN, COMSIG_AIRLOCK_CLOSE) else sig_list += list(COMSIG_ATOM_ATTACK_HAND) target.AddComponent(/datum/component/sound_player, amount = (rand(15,20)), signal_or_sig_list = sig_list) installed_cartridge.charges -- return TRUE // access to status display signals /obj/item/pda/attackby(obj/item/C, mob/user, params) if(istype(C, /obj/item/cartridge)) if(!user.transferItemToLoc(C, src)) return eject_cart(user) cartridge = C cartridge.host_pda = src to_chat(user, "You insert [cartridge] into [src].") updateSelfDialog() update_appearance() playsound(src, 'sound/machines/pda_button1.ogg', 50, TRUE) else if(istype(C, /obj/item/card/id)) var/obj/item/card/id/idcard = C if(!idcard.registered_name) to_chat(user, "\The [src] rejects the ID!") if(!silent) playsound(src, 'sound/machines/terminal_error.ogg', 50, TRUE) return if(!owner) owner = idcard.registered_name ownjob = idcard.assignment update_label() to_chat(user, "Card scanned.") if(!silent) playsound(src, 'sound/machines/terminal_success.ogg', 50, TRUE) else if(!id_check(user, idcard)) return to_chat(user, "You put the ID into \the [src]'s slot.") updateSelfDialog()//Update self dialog on success. return //Return in case of failed check or when successful. updateSelfDialog()//For the non-input related code. else if(istype(C, /obj/item/paicard) && !pai) if(!user.transferItemToLoc(C, src)) return pai = C to_chat(user, "You slot \the [C] into [src].") update_appearance() updateUsrDialog() else if(is_type_in_list(C, contained_item)) //Checks if there is a pen if(inserted_item) to_chat(user, "There is already \a [inserted_item] in \the [src]!") else if(!user.transferItemToLoc(C, src)) return to_chat(user, "You slide \the [C] into \the [src].") inserted_item = C update_appearance() playsound(src, 'sound/machines/pda_button1.ogg', 50, TRUE) else if(istype(C, /obj/item/photo)) var/obj/item/photo/P = C picture = P.picture to_chat(user, "You scan \the [C].") // Check to see if we have an ID inside, and a valid input for money else if(id && iscash(C)) id.attackby(C, user) // If we do, try and put that attacking object in else return ..() /obj/item/pda/attack(mob/living/carbon/C, mob/living/user) if(istype(C)) switch(scanmode) if(PDA_SCANNER_MEDICAL) C.visible_message("[user] analyzes [C]'s vitals.") healthscan(user, C, 1) add_fingerprint(user) if(PDA_SCANNER_HALOGEN) C.visible_message("[user] analyzes [C]'s radiation levels.") user.show_message("Analyzing Results for [C]:") if(C.radiation) user.show_message("\green Radiation Level: \black [C.radiation]") else user.show_message("No radiation detected.") /obj/item/pda/afterattack(atom/A as mob|obj|turf|area, mob/user, proximity) . = ..() if(!proximity) return switch(scanmode) if(PDA_SCANNER_REAGENT) 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].") if(PDA_SCANNER_GAS) A.analyzer_act(user, src) if (!scanmode && istype(A, /obj/item/paper) && owner) var/obj/item/paper/PP = A if (!PP.info) to_chat(user, "Unable to scan! Paper is blank.") return notehtml = PP.info note = replacetext(notehtml, "
", "\[br\]") note = replacetext(note, "
  • ", "\[*\]") note = replacetext(note, "", "\[/list\]") note = html_encode(note) notescanned = TRUE to_chat(user, "Paper scanned. Saved to PDA's notekeeper." ) /obj/item/pda/proc/explode() //This needs tuning. var/turf/T = get_turf(src) if (ismob(loc)) var/mob/M = loc M.show_message("Your [src] explodes!", MSG_VISUAL, "You hear a loud *pop*!", MSG_AUDIBLE) else visible_message("[src] explodes!", "You hear a loud *pop*!") if(T) T.hotspot_expose(700,125) if(istype(cartridge, /obj/item/cartridge/virus/syndicate)) explosion(src, devastation_range = -1, heavy_impact_range = 1, light_impact_range = 3, flash_range = 4) else explosion(src, devastation_range = -1, heavy_impact_range = -1, light_impact_range = 2, flash_range = 3) qdel(src) return /obj/item/pda/Destroy() GLOB.PDAs -= src if(istype(id)) QDEL_NULL(id) if(istype(cartridge)) QDEL_NULL(cartridge) if(istype(pai)) QDEL_NULL(pai) if(istype(inserted_item)) QDEL_NULL(inserted_item) return ..() //AI verb and proc for sending PDA messages. /obj/item/pda/ai/verb/cmd_toggle_pda_receiver() set category = "AI Commands" set name = "PDA - Toggle Sender/Receiver" if(usr.stat == DEAD) return //won't work if dead var/mob/living/silicon/S = usr if(istype(S) && !isnull(S.aiPDA)) S.aiPDA.toff = !S.aiPDA.toff to_chat(usr, "PDA sender/receiver toggled [(S.aiPDA.toff ? "Off" : "On")]!") else to_chat(usr, "You do not have a PDA. You should make an issue report about this.") /obj/item/pda/ai/verb/cmd_toggle_pda_silent() set category = "AI Commands" set name = "PDA - Toggle Ringer" if(usr.stat == DEAD) return //won't work if dead var/mob/living/silicon/S = usr if(istype(S) && !isnull(S.aiPDA)) //0 S.aiPDA.silent = !S.aiPDA.silent to_chat(usr, "PDA ringer toggled [(S.aiPDA.silent ? "Off" : "On")]!") else to_chat(usr, "You do not have a PDA. You should make an issue report about this.") /mob/living/silicon/proc/cmd_send_pdamesg(mob/user) var/list/plist = list() var/list/namecounts = list() if(aiPDA.toff) to_chat(user, "Turn on your receiver in order to send messages.") return for (var/obj/item/pda/P in get_viewable_pdas()) if (P == src) continue else if (P == aiPDA) continue plist[avoid_assoc_duplicate_keys(P.owner, namecounts)] = P var/c = input(user, "Please select a PDA") as null|anything in sortList(plist) if (!c) return var/selected = plist[c] if(aicamera.stored.len) var/add_photo = input(user,"Do you want to attach a photo?","Photo","No") as null|anything in list("Yes","No") if(add_photo=="Yes") var/datum/picture/Pic = aicamera.selectpicture(user) aiPDA.picture = Pic if(incapacitated()) return aiPDA.create_message(src, selected) /mob/living/silicon/proc/cmd_show_message_log(mob/user) if(incapacitated()) return if(!isnull(aiPDA)) var/HTML = "AI PDA Message Log[aiPDA.tnote]" user << browse(HTML, "window=log;size=400x444;border=1;can_resize=1;can_close=1;can_minimize=0") else to_chat(user, "You do not have a PDA! You should make an issue report about this.") // Pass along the pulse to atoms in contents, largely added so pAIs are vulnerable to EMP /obj/item/pda/emp_act(severity) . = ..() if (!(. & EMP_PROTECT_CONTENTS)) for(var/atom/A in src) A.emp_act(severity) if (!(. & EMP_PROTECT_SELF)) emped++ addtimer(CALLBACK(src, .proc/emp_end), 200 * severity) /obj/item/pda/proc/emp_end() emped-- /proc/get_viewable_pdas(sort_by_job = FALSE) . = list() // Returns a list of PDAs which can be viewed from another PDA/message monitor., var/sortmode if(sort_by_job) sortmode = /proc/cmp_pdajob_asc else sortmode = /proc/cmp_pdaname_asc for(var/obj/item/pda/P in sortList(GLOB.PDAs, sortmode)) if(!P.owner || P.toff || P.hidden) continue . += P /obj/item/pda/proc/pda_no_detonate() SIGNAL_HANDLER return COMPONENT_PDA_NO_DETONATE #undef PDA_SCANNER_NONE #undef PDA_SCANNER_MEDICAL #undef PDA_SCANNER_FORENSICS #undef PDA_SCANNER_REAGENT #undef PDA_SCANNER_HALOGEN #undef PDA_SCANNER_GAS #undef PDA_SPAM_DELAY