//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_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, 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) /// String name of owner var/owner = null /// Access level defined by cartridge var/default_cartridge = 0 /// Current cartridge var/obj/item/cartridge/cartridge = null /// Controls what menu the PDA will display. 0 is hub; the rest are either built in or based on cartridge. var/mode = 0 /// Icon to be overlayed for message alerts. Taken from the pda icon file. var/icon_alert = "pda-r" /// Icon to be overlayed when an active pAI is slotted in. var/icon_pai = "pai_overlay" /// Same as above but for an inactive pAI. var/icon_inactive_pai = "pai_off_overlay" /** * 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_index = 0 /// The currently selected font. var/font_mode = "font-family:monospace;" /// The currently selected background color. var/background_color = "#808000" #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 ///account id of the ID held var/account_id 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/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(span_suicide("[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) . += span_notice("Alt-click to remove the ID.") //won't name ID on examine in case it's stolen if(inserted_item && (!isturf(loc))) . += span_notice("Ctrl-click to remove [inserted_item].") //traitor pens are disguised so we're fine naming them on examine if((!isnull(cartridge))) . += span_notice("Ctrl+Shift-click to remove the cartridge.") //won't name cart on examine in case it's Detomatix /obj/item/pda/Initialize(mapload) . = ..() 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.read_preference(/datum/preference/color/pda_color) switch(user.client.prefs.read_preference(/datum/preference/choiced/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() . = ..() var/init_icon = initial(icon) if(!init_icon) return if(id) . += mutable_appearance(init_icon, "id_overlay") if(inserted_item) . += mutable_appearance(init_icon, "insert_overlay") if(light_on) . += mutable_appearance(init_icon, "light_overlay") if(pai) if(pai.pai) . += mutable_appearance(init_icon, icon_pai) else . += mutable_appearance(init_icon, icon_inactive_pai) /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, span_warning("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, span_warning("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 += "It has been [ROUND_TIME] since the emergency shuttle was last called." 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(!href_list["close"] && usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) 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("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 = span_boldnotice("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, span_warning("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, span_notice("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, span_notice("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 var/list/filter_result = is_ic_filtered_for_pdas(message) if (filter_result) REPORT_CHAT_FILTER_TO_USER(user, filter_result) return var/list/soft_filter_result = is_soft_ic_filtered_for_pdas(message) if (soft_filter_result) if(tgui_alert(usr,"Your message contains \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\". \"[soft_filter_result[CHAT_FILTER_INDEX_REASON]]\", Are you sure you want to send it?", "Soft Blocked Word", list("Yes", "No")) != "Yes") return message_admins("[ADMIN_LOOKUPFLW(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term in PDA messages. Message: \"[html_encode(message)]\"") log_admin_private("[key_name(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term in PDA messages. Message: \"[message]\"") 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, span_notice("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 = span_name("[owner] PDA Message --> [span_name("[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, span_info("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,span_warning("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, span_warning("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() update_action_buttons(force = TRUE) /// 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(span_danger("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, span_notice("You remove [inserted_item] from [src].")) inserted_item = null update_appearance() playsound(src, 'sound/machines/pda_button2.ogg', 50, TRUE) else to_chat(user, span_warning("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, span_notice("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, span_notice("Out of charges.")) return ..() to_chat(user, span_notice("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, span_notice("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, span_warning("\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, span_notice("Card scanned.")) if(!silent) playsound(src, 'sound/machines/terminal_success.ogg', 50, TRUE) else if(!id_check(user, idcard)) return to_chat(user, span_notice("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, span_notice("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, span_warning("There is already \a [inserted_item] in \the [src]!")) else if(!user.transferItemToLoc(C, src)) return to_chat(user, span_notice("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, span_notice("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(span_notice("[user] analyzes [C]'s vitals.")) healthscan(user, C, 1) add_fingerprint(user) /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, span_notice("[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.")) for (var/re in A.reagents.reagent_list) to_chat(user, span_notice("\t [re]")) else to_chat(user, span_notice("No active chemical agents found in [A].")) else to_chat(user, span_notice("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/paper = A if (!paper.get_info_length()) to_chat(user, span_warning("Unable to scan! Paper is blank.")) return notehtml = paper.info if(paper.add_info) for(var/index in 1 to length(paper.add_info)) var/list/style = paper.add_info_style[index] notehtml += PAPER_MARK_TEXT(paper.add_info[index], style[ADD_INFO_COLOR], style[ADD_INFO_FONT]) note = replacetext(notehtml, "
", "\[br\]") note = replacetext(note, "
  • ", "\[*\]") note = replacetext(note, "", "\[/list\]") note = html_encode(note) notescanned = TRUE to_chat(user, span_notice("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(span_userdanger("Your [src] explodes!"), MSG_VISUAL, span_warning("You hear a loud *pop*!"), MSG_AUDIBLE) else visible_message(span_danger("[src] explodes!"), span_warning("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, span_notice("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, span_notice("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, span_alert("Turn on your receiver in order to send messages.")) return for (var/obj/item/pda/pda as anything in get_viewable_pdas()) if (pda == src) continue else if (pda == aiPDA) continue plist[avoid_assoc_duplicate_keys(pda.owner, namecounts)] = pda var/choice = tgui_input_list(user, "Please select a PDA", "PDA Messenger", sort_list(plist)) if (!choice) return var/selected = plist[choice] if(aicamera.stored.len) var/add_photo = tgui_alert(user,"Do you want to attach a photo?", "PDA Messenger", 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, span_warning("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 sort_list(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_GAS #undef PDA_SPAM_DELAY