Files
Bubberstation/code/game/objects/items/devices/PDA/PDA.dm
Dax Dupont 8adbe2ed8e Adds a cooldown to PDA send to all messages. (#37422)
* Adds a cooldown to PDA send to all messages.

* aaa

aaa

* forgot this one

* condom
2018-04-30 02:09:10 -04:00

1020 lines
36 KiB
Plaintext

//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 PDA"
desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge."
icon = 'icons/obj/pda.dmi'
icon_state = "pda"
item_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
flags_1 = NOBLUDGEON_1
w_class = WEIGHT_CLASS_TINY
slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT
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
//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/fon = FALSE //Is the flashlight function on?
var/f_lum = 2.3 //Luminosity for the flashlight function
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/lock_code = "" // Lockcode to unlock uplink
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/detonatable = TRUE // Can the PDA be blown up?
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/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/icon/photo //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("<span class='suicide'>[user] is sending a message to the Grim Reaper! It looks like [user.p_theyre()] trying to commit suicide!</span>")
tnote += "<i><b>&rarr; To The Grim Reaper:</b></i><br>[deathMessage]<br>"//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
else
to_chat(user, "<span class='notice'>Alt-click to remove contents.</span>")
/obj/item/pda/Initialize()
. = ..()
if(fon)
set_light(f_lum)
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)
update_icon()
/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/GetID()
return id
/obj/item/pda/update_icon()
cut_overlays()
var/mutable_appearance/overlay = new()
overlay.pixel_x = overlays_x_offset
if(id)
overlay.icon_state = "id_overlay"
add_overlay(new /mutable_appearance(overlay))
if(inserted_item)
overlay.icon_state = "insert_overlay"
add_overlay(new /mutable_appearance(overlay))
if(fon)
overlay.icon_state = "light_overlay"
add_overlay(new /mutable_appearance(overlay))
if(pai)
if(pai.pai)
overlay.icon_state = "pai_overlay"
add_overlay(new /mutable_appearance(overlay))
else
overlay.icon_state = "pai_off_overlay"
add_overlay(new /mutable_appearance(overlay))
/obj/item/pda/MouseDrop(obj/over_object, src_location, over_location)
var/mob/M = usr
if((!istype(over_object, /obj/screen)) && usr.canUseTopic(src))
return attack_self(M)
return ..()
/obj/item/pda/attack_self(mob/user)
if(!user.IsAdvancedToolUser())
to_chat(user, "<span class='warning'>You don't have the dexterity to do this!</span>")
return
. = ..()
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/pda)
assets.send(user)
user.set_machine(src)
var/dat = "<!DOCTYPE html><html><head><title>Personal Data Assistant</title><link href=\"https://fonts.googleapis.com/css?family=Orbitron|Share+Tech+Mono|VT323\" rel=\"stylesheet\"></head><body bgcolor=\"" + background_color + "\"><style>body{" + font_mode + "}ul,ol{list-style-type: none;}a, a:link, a:visited, a:active, a:hover { color: #000000;text-decoration:none; }img {border-style:none;}a img{padding-right: 9px;}</style>"
dat += "<a href='byond://?src=[REF(src)];choice=Refresh'><img src=pda_refresh.png>Refresh</a>"
if ((!isnull(cartridge)) && (mode == 0))
dat += " | <a href='byond://?src=[REF(src)];choice=Eject'><img src=pda_eject.png>Eject [cartridge]</a>"
if (mode)
dat += " | <a href='byond://?src=[REF(src)];choice=Return'><img src=pda_menu.png>Return</a>"
if (mode == 0)
dat += "<div align=\"center\">"
dat += "<br><a href='byond://?src=[REF(src)];choice=Toggle_Font'>Toggle Font</a>"
dat += " | <a href='byond://?src=[REF(src)];choice=Change_Color'>Change Color</a>"
dat += " | <a href='byond://?src=[REF(src)];choice=Toggle_Underline'>Toggle Underline</a>" //underline button
dat += "</div>"
dat += "<br>"
if (!owner)
dat += "Warning: No owner information entered. Please swipe card.<br><br>"
dat += "<a href='byond://?src=[REF(src)];choice=Refresh'><img src=pda_refresh.png>Retry</a>"
else
switch (mode)
if (0)
dat += "<h2>PERSONAL DATA ASSISTANT v.1.2</h2>"
dat += "Owner: [owner], [ownjob]<br>"
dat += text("ID: <a href='?src=[REF(src)];choice=Authenticate'>[id ? "[id.registered_name], [id.assignment]" : "----------"]")
dat += text("<br><a href='?src=[REF(src)];choice=UpdateInfo'>[id ? "Update PDA Info" : ""]</A><br><br>")
dat += "[station_time_timestamp()]<br>" //:[world.time / 100 % 6][world.time / 100 % 10]"
dat += "[time2text(world.realtime, "MMM DD")] [GLOB.year_integer+540]"
dat += "<br><br>"
dat += "<h4>General Functions</h4>"
dat += "<ul>"
dat += "<li><a href='byond://?src=[REF(src)];choice=1'><img src=pda_notes.png>Notekeeper</a></li>"
dat += "<li><a href='byond://?src=[REF(src)];choice=2'><img src=pda_mail.png>Messenger</a></li>"
if (cartridge)
if (cartridge.access & CART_CLOWN)
dat += "<li><a href='byond://?src=[REF(src)];choice=Honk'><img src=pda_honk.png>Honk Synthesizer</a></li>"
dat += "<li><a href='byond://?src=[REF(src)];choice=Trombone'><img src=pda_honk.png>Sad Trombone</a></li>"
if (cartridge.access & CART_MANIFEST)
dat += "<li><a href='byond://?src=[REF(src)];choice=41'><img src=pda_notes.png>View Crew Manifest</a></li>"
if(cartridge.access & CART_STATUS_DISPLAY)
dat += "<li><a href='byond://?src=[REF(src)];choice=42'><img src=pda_status.png>Set Status Display</a></li>"
dat += "</ul>"
if (cartridge.access & CART_ENGINE)
dat += "<h4>Engineering Functions</h4>"
dat += "<ul>"
dat += "<li><a href='byond://?src=[REF(src)];choice=43'><img src=pda_power.png>Power Monitor</a></li>"
dat += "</ul>"
if (cartridge.access & CART_MEDICAL)
dat += "<h4>Medical Functions</h4>"
dat += "<ul>"
dat += "<li><a href='byond://?src=[REF(src)];choice=44'><img src=pda_medical.png>Medical Records</a></li>"
dat += "<li><a href='byond://?src=[REF(src)];choice=Medical Scan'><img src=pda_scanner.png>[scanmode == 1 ? "Disable" : "Enable"] Medical Scanner</a></li>"
dat += "</ul>"
if (cartridge.access & CART_SECURITY)
dat += "<h4>Security Functions</h4>"
dat += "<ul>"
dat += "<li><a href='byond://?src=[REF(src)];choice=45'><img src=pda_cuffs.png>Security Records</A></li>"
dat += "</ul>"
if(cartridge.access & CART_QUARTERMASTER)
dat += "<h4>Quartermaster Functions:</h4>"
dat += "<ul>"
dat += "<li><a href='byond://?src=[REF(src)];choice=47'><img src=pda_crate.png>Supply Records</A></li>"
dat += "</ul>"
dat += "</ul>"
dat += "<h4>Utilities</h4>"
dat += "<ul>"
if (cartridge)
if(cartridge.bot_access_flags)
dat += "<li><a href='byond://?src=[REF(src)];choice=54'><img src=pda_medbot.png>Bots Access</a></li>"
if (cartridge.access & CART_JANITOR)
dat += "<li><a href='byond://?src=[REF(src)];choice=49'><img src=pda_bucket.png>Custodial Locator</a></li>"
if (istype(cartridge.radio))
dat += "<li><a href='byond://?src=[REF(src)];choice=40'><img src=pda_signaler.png>Signaler System</a></li>"
if (cartridge.access & CART_NEWSCASTER)
dat += "<li><a href='byond://?src=[REF(src)];choice=53'><img src=pda_notes.png>Newscaster Access </a></li>"
if (cartridge.access & CART_REAGENT_SCANNER)
dat += "<li><a href='byond://?src=[REF(src)];choice=Reagent Scan'><img src=pda_reagent.png>[scanmode == 3 ? "Disable" : "Enable"] Reagent Scanner</a></li>"
if (cartridge.access & CART_ENGINE)
dat += "<li><a href='byond://?src=[REF(src)];choice=Halogen Counter'><img src=pda_reagent.png>[scanmode == 4 ? "Disable" : "Enable"] Halogen Counter</a></li>"
if (cartridge.access & CART_ATMOS)
dat += "<li><a href='byond://?src=[REF(src)];choice=Gas Scan'><img src=pda_reagent.png>[scanmode == 5 ? "Disable" : "Enable"] Gas Scanner</a></li>"
if (cartridge.access & CART_REMOTE_DOOR)
dat += "<li><a href='byond://?src=[REF(src)];choice=Toggle Door'><img src=pda_rdoor.png>Toggle Remote Door</a></li>"
if (cartridge.access & CART_DRONEPHONE)
dat += "<li><a href='byond://?src=[REF(src)];choice=Drone Phone'><img src=pda_dronephone.png>Drone Phone</a></li>"
dat += "<li><a href='byond://?src=[REF(src)];choice=3'><img src=pda_atmos.png>Atmospheric Scan</a></li>"
dat += "<li><a href='byond://?src=[REF(src)];choice=Light'><img src=pda_flashlight.png>[fon ? "Disable" : "Enable"] Flashlight</a></li>"
if (pai)
if(pai.loc != src)
pai = null
update_icon()
else
dat += "<li><a href='byond://?src=[REF(src)];choice=pai;option=1'>pAI Device Configuration</a></li>"
dat += "<li><a href='byond://?src=[REF(src)];choice=pai;option=2'>Eject pAI Device</a></li>"
dat += "</ul>"
if (1)
dat += "<h4><img src=pda_notes.png> Notekeeper V2.2</h4>"
dat += "<a href='byond://?src=[REF(src)];choice=Edit'>Edit</a><br>"
if(notescanned)
dat += "(This is a scanned image, editing it may cause some text formatting to change.)<br>"
dat += "<HR><font face=\"[PEN_FONT]\">[(!notehtml ? note : notehtml)]</font>"
if (2)
dat += "<h4><img src=pda_mail.png> SpaceMessenger V3.9.6</h4>"
dat += "<a href='byond://?src=[REF(src)];choice=Toggle Ringer'><img src=pda_bell.png>Ringer: [silent == 1 ? "Off" : "On"]</a> | "
dat += "<a href='byond://?src=[REF(src)];choice=Toggle Messenger'><img src=pda_mail.png>Send / Receive: [toff == 1 ? "Off" : "On"]</a> | "
dat += "<a href='byond://?src=[REF(src)];choice=Ringtone'><img src=pda_bell.png>Set Ringtone</a> | "
dat += "<a href='byond://?src=[REF(src)];choice=21'><img src=pda_mail.png>Messages</a><br>"
if(cartridge)
dat += cartridge.message_header()
dat += "<h4><img src=pda_menu.png> Detected PDAs</h4>"
dat += "<ul>"
var/count = 0
if (!toff)
for (var/obj/item/pda/P in sortNames(get_viewable_pdas()))
if (P == src)
continue
dat += "<li><a href='byond://?src=[REF(src)];choice=Message;target=[REF(P)]'>[P]</a>"
if(cartridge)
dat += cartridge.message_special(P)
dat += "</li>"
count++
dat += "</ul>"
if (count == 0)
dat += "None detected.<br>"
else if(cartridge && cartridge.spam_enabled)
dat += "<a href='byond://?src=[REF(src)];choice=MessageAll'>Send To All</a>"
if(21)
dat += "<h4><img src=pda_mail.png> SpaceMessenger V3.9.6</h4>"
dat += "<a href='byond://?src=[REF(src)];choice=Clear'><img src=pda_blank.png>Clear Messages</a>"
dat += "<h4><img src=pda_mail.png> Messages</h4>"
dat += tnote
dat += "<br>"
if (3)
dat += "<h4><img src=pda_atmos.png> Atmospheric Readings</h4>"
var/turf/T = user.loc
if (isnull(T))
dat += "Unable to obtain a reading.<br>"
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<br>"
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)]%<br>"
dat += "Temperature: [round(environment.temperature-T0C)]&deg;C<br>"
dat += "<br>"
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 += "</body></html>"
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) && !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 ("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 ("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("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 ("Authenticate")//Checks for ID
id_check(U)
if("UpdateInfo")
ownjob = id.assignment
if(istype(id, /obj/item/card/id/syndicate))
owner = id.registered_name
update_label()
if("Eject")//Ejects the cart, only done from hub.
if (!isnull(cartridge))
U.put_in_hands(cartridge)
to_chat(U, "<span class='notice'>You remove [cartridge] from [src].</span>")
scanmode = PDA_SCANNER_NONE
cartridge.host_pda = null
cartridge = null
update_icon()
//MENU FUNCTIONS===================================
if("0")//Hub
mode = 0
if("1")//Notes
mode = 1
if("2")//Messenger
mode = 2
if("21")//Read messeges
mode = 21
if("3")//Atmos scan
mode = 3
if("4")//Redirects to hub
mode = 0
//MAIN FUNCTIONS===================================
if("Light")
if(fon)
fon = FALSE
set_light(0)
else if(f_lum)
fon = TRUE
set_light(f_lum)
update_icon()
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("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("Honk")
if ( !(last_noise && world.time < last_noise + 20) )
playsound(src, 'sound/items/bikehorn.ogg', 50, 1)
last_noise = world.time
if("Trombone")
if ( !(last_noise && world.time < last_noise + 20) )
playsound(src, 'sound/misc/sadtrombone.ogg', 50, 1)
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("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 class='boldnotice'>NON-DRONE PING: [U.name]: [alert_s] priority alert in [A.name]!</span>"
_alert_drones(msg, TRUE, U)
to_chat(U, msg)
//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 = input(U, "Please enter new ringtone", name, ttone) as text
if(in_range(src, U) && loc == U && t)
GET_COMPONENT(hidden_uplink, /datum/component/uplink)
if(hidden_uplink && (trim(lowertext(t)) == trim(lowertext(lock_code))))
hidden_uplink.locked = FALSE
hidden_uplink.interact(U)
to_chat(U, "The PDA softly beeps.")
U << browse(null, "window=pda")
src.mode = 0
else
t = copytext(sanitize(t), 1, 20)
ttone = t
else
U << browse(null, "window=pda")
return
if("Message")
src.create_message(U, locate(href_list["target"]))
if("MessageAll")
src.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
var/turf/T = get_turf(src.loc)
if(T)
pai.forceMove(T)
//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_icon()
if ((honkamt > 0) && (prob(60)))//For clown virus.
honkamt--
playsound(src, 'sound/items/bikehorn.ogg', 30, 1)
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()
if (id)
if (ismob(loc))
var/mob/M = loc
M.put_in_hands(id)
to_chat(usr, "<span class='notice'>You remove the ID from the [name].</span>")
else
id.forceMove(drop_location())
id = null
update_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 (!in_range(src, U) && loc != U)
return
if(!U.canUseTopic(src))
return
if(emped)
t = Gibberish(t, 100)
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
// 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/pda/signal = new(src, list(
"name" = "[owner]",
"job" = "[ownjob]",
"message" = message,
"targets" = string_targets
))
if (photo)
signal.data["photo"] = photo
signal.send_to_receivers()
// If it didn't reach, note that fact
if (!signal.data["done"])
to_chat(user, "<span class='notice'>ERROR: Server isn't responding.</span>")
return
var/target_text = signal.format_target()
// Log it in our logs
tnote += "<i><b>&rarr; To [target_text]:</b></i><br>[signal.format_message()]<br>"
// Show it to ghosts
var/ghost_message = "<span class='name'>[owner] </span><span class='game say'>PDA Message</span> --> <span class='name'>[target_text]</span>: <span class='message'>[signal.format_message()]</span>"
for(var/mob/M in GLOB.player_list)
if(isobserver(M) && M.client && (M.client.prefs.chat_toggles & CHAT_GHOSTPDA))
to_chat(M, "[FOLLOW_LINK(M, user)] [ghost_message]")
// Log in the talk log
log_talk(user, "[key_name(user)] (PDA: [initial(name)]) sent \"[message]\" to [target_text]", LOGPDA)
to_chat(user, "<span class='info'>Message sent to [target_text]: \"[message]\"</span>")
// Reset the photo
photo = null
last_text = world.time
if (everyone)
last_everyone = world.time
/obj/item/pda/proc/receive_message(datum/signal/subspace/pda/signal)
tnote += "<i><b>&larr; From <a href='byond://?src=[REF(src)];choice=Message;target=[REF(signal.source)]'>[signal.data["name"]]</a> ([signal.data["job"]]):</b></i><br>[signal.format_message()]<br>"
if (!silent)
playsound(src, 'sound/machines/twobeep.ogg', 50, 1)
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 != UNCONSCIOUS)
var/hrefstart
var/hrefend
if (isAI(L))
hrefstart = "<a href='?src=[REF(L)];track=[html_encode(signal.data["name"])]'>"
hrefend = "</a>"
to_chat(L, "[icon2html(src)] <b>Message from [hrefstart][signal.data["name"]] ([signal.data["job"]])[hrefend], </b>[signal.format_message()] (<a href='byond://?src=[REF(src)];choice=Message;skiprefresh=1;target=[REF(signal.source)]'>Reply</a>)")
update_icon()
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 class='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()
..()
if(issilicon(usr))
return
if(usr.canUseTopic(src))
if(id)
remove_id()
else
remove_pen()
/obj/item/pda/verb/verb_remove_id()
set category = "Object"
set name = "Eject ID"
set src in usr
if(issilicon(usr))
return
if (usr.canUseTopic(src))
if(id)
remove_id()
else
to_chat(usr, "<span class='warning'>This PDA does not have an ID in it!</span>")
/obj/item/pda/verb/verb_remove_pen()
set category = "Object"
set name = "Remove Pen"
set src in usr
if(issilicon(usr))
return
if (usr.canUseTopic(src))
remove_pen()
/obj/item/pda/proc/remove_pen()
if(inserted_item)
if(ismob(loc))
var/mob/M = loc
M.put_in_hands(inserted_item)
else
inserted_item.forceMove(get_turf(src))
to_chat(usr, "<span class='notice'>You remove \the [inserted_item] from \the [src].</span>")
inserted_item = null
update_icon()
else
to_chat(usr, "<span class='warning'>This PDA does not have a pen in it!</span>")
//trying to insert or remove an id
/obj/item/pda/proc/id_check(mob/user, obj/item/card/id/I)
if(!I)
if(id)
remove_id()
return 1
else
var/obj/item/card/id/C = user.get_active_held_item()
if(istype(C))
I = C
if(I && I.registered_name)
if(!user.transferItemToLoc(I, src))
return 0
var/obj/old_id = id
id = I
if(old_id)
user.put_in_hands(old_id)
update_icon()
return 1
// access to status display signals
/obj/item/pda/attackby(obj/item/C, mob/user, params)
if(istype(C, /obj/item/cartridge) && !cartridge)
if(!user.transferItemToLoc(C, src))
return
cartridge = C
cartridge.host_pda = src
to_chat(user, "<span class='notice'>You insert [cartridge] into [src].</span>")
update_icon()
else if(istype(C, /obj/item/card/id))
var/obj/item/card/id/idcard = C
if(!idcard.registered_name)
to_chat(user, "<span class='warning'>\The [src] rejects the ID!</span>")
return
if(!owner)
owner = idcard.registered_name
ownjob = idcard.assignment
update_label()
to_chat(user, "<span class='notice'>Card scanned.</span>")
else
//Basic safety check. If either both objects are held by user or PDA is on ground and card is in hand.
if(((src in user.contents) || (isturf(loc) && in_range(src, user))) && (C in user.contents))
if(!id_check(user, idcard))
return
to_chat(user, "<span class='notice'>You put the ID into \the [src]'s slot.</span>")
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) && !src.pai)
if(!user.transferItemToLoc(C, src))
return
pai = C
to_chat(user, "<span class='notice'>You slot \the [C] into [src].</span>")
update_icon()
updateUsrDialog()
else if(is_type_in_list(C, contained_item)) //Checks if there is a pen
if(inserted_item)
to_chat(user, "<span class='warning'>There is already \a [inserted_item] in \the [src]!</span>")
else
if(!user.transferItemToLoc(C, src))
return
to_chat(user, "<span class='notice'>You slide \the [C] into \the [src].</span>")
inserted_item = C
update_icon()
else if(istype(C, /obj/item/photo))
var/obj/item/photo/P = C
photo = P.img
to_chat(user, "<span class='notice'>You scan \the [C].</span>")
else
return ..()
/obj/item/pda/attack(mob/living/carbon/C, mob/living/user)
if(istype(C))
switch(scanmode)
if(1)
C.visible_message("<span class='alert'>[user] has analyzed [C]'s vitals!</span>")
healthscan(user, C, 1)
add_fingerprint(user)
if(4)
C.visible_message("<span class='warning'>[user] has analyzed [C]'s radiation levels!</span>")
user.show_message("<span class='notice'>Analyzing Results for [C]:</span>")
if(C.radiation)
user.show_message("\green Radiation Level: \black [C.radiation]")
else
user.show_message("<span class='notice'>No radiation detected.</span>")
/obj/item/pda/afterattack(atom/A as mob|obj|turf|area, mob/user, proximity)
if(!proximity)
return
switch(scanmode)
if(3)
if(!isnull(A.reagents))
if(A.reagents.reagent_list.len > 0)
var/reagents_length = A.reagents.reagent_list.len
to_chat(user, "<span class='notice'>[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.</span>")
for (var/re in A.reagents.reagent_list)
to_chat(user, "<span class='notice'>\t [re]</span>")
else
to_chat(user, "<span class='notice'>No active chemical agents found in [A].</span>")
else
to_chat(user, "<span class='notice'>No significant chemical agents found in [A].</span>")
if(5)
if (istype(A, /obj/item/tank))
var/obj/item/tank/T = A
atmosanalyzer_scan(T.air_contents, user, T)
else if (istype(A, /obj/machinery/portable_atmospherics))
var/obj/machinery/portable_atmospherics/PA = A
atmosanalyzer_scan(PA.air_contents, user, PA)
else if (istype(A, /obj/machinery/atmospherics/pipe))
var/obj/machinery/atmospherics/pipe/P = A
atmosanalyzer_scan(P.parent.air, user, P)
else if (istype(A, /obj/machinery/atmospherics/components/unary))
var/obj/machinery/atmospherics/components/unary/U = A
atmosanalyzer_scan(U.airs[1], user, U)
else if (istype(A, /obj/machinery/power/rad_collector))
var/obj/machinery/power/rad_collector/RC = A
if(RC.loaded_tank)
atmosanalyzer_scan(RC.loaded_tank.air_contents, user, RC)
else if (istype(A, /obj/item/flamethrower))
var/obj/item/flamethrower/F = A
if(F.ptank)
atmosanalyzer_scan(F.ptank.air_contents, user, F)
if (!scanmode && istype(A, /obj/item/paper) && owner)
var/obj/item/paper/PP = A
if (!PP.info)
to_chat(user, "<span class='warning'>Unable to scan! Paper is blank.</span>")
return
notehtml = PP.info
note = replacetext(notehtml, "<BR>", "\[br\]")
note = replacetext(note, "<li>", "\[*\]")
note = replacetext(note, "<ul>", "\[list\]")
note = replacetext(note, "</ul>", "\[/list\]")
note = html_encode(note)
notescanned = TRUE
to_chat(user, "<span class='notice'>Paper scanned. Saved to PDA's notekeeper.</span>" )
/obj/item/pda/proc/explode() //This needs tuning.
if(!detonatable)
return
var/turf/T = get_turf(src)
if (ismob(loc))
var/mob/M = loc
M.show_message("<span class='userdanger'>Your [src] explodes!</span>", 1)
else
visible_message("<span class='danger'>[src] explodes!</span>", "<span class='warning'>You hear a loud *pop*!</span>")
if(T)
T.hotspot_expose(700,125)
if(istype(cartridge, /obj/item/cartridge/virus/syndicate))
explosion(T, -1, 1, 3, 4)
else
explosion(T, -1, -1, 2, 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.
/mob/living/silicon/ai/proc/cmd_send_pdamesg(mob/user)
var/list/plist = list()
var/list/namecounts = list()
if(src.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 == src.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.aipictures.len>0)
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(aicamera)
src.aiPDA.photo = Pic.fields["img"]
if(incapacitated())
return
src.aiPDA.create_message(src, selected)
/mob/living/silicon/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
if(!isnull(aiPDA))
aiPDA.toff = !aiPDA.toff
to_chat(usr, "<span class='notice'>PDA sender/receiver toggled [(aiPDA.toff ? "Off" : "On")]!</span>")
else
to_chat(usr, "You do not have a PDA. You should make an issue report about this.")
/mob/living/silicon/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
if(!isnull(aiPDA))
//0
aiPDA.silent = !aiPDA.silent
to_chat(usr, "<span class='notice'>PDA ringer toggled [(aiPDA.silent ? "Off" : "On")]!</span>")
else
to_chat(usr, "You do not have a PDA. You should make an issue report about this.")
/mob/living/silicon/ai/proc/cmd_show_message_log(mob/user)
if(incapacitated())
return
if(!isnull(aiPDA))
var/HTML = "<html><head><title>AI PDA Message Log</title></head><body>[aiPDA.tnote]</body></html>"
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)
for(var/atom/A in src)
A.emp_act(severity)
emped += 1
spawn(200 * severity)
emped -= 1
/proc/get_viewable_pdas()
. = list()
// Returns a list of PDAs which can be viewed from another PDA/message monitor.
for(var/obj/item/pda/P in GLOB.PDAs)
if(!P.owner || P.toff || P.hidden)
continue
. += P
#undef PDA_SCANNER_NONE
#undef PDA_SCANNER_MEDICAL
#undef PDA_SCANNER_REAGENT
#undef PDA_SCANNER_HALOGEN
#undef PDA_SCANNER_GAS
#undef PDA_SPAM_DELAY