mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-05-31 19:47:22 +01:00
d865f6fa9a
* pAIs control a PDA they're inserted into (#78103) ## About The Pull Request This PR lets pAIs inserted into a PDA to control the PDA of their owner. They can see their own pAI configurations but cannot edit any of it. They also can't eject themselves from it. This means they can receive and send PDA messages as their owner, acting like a personal secretary This also adds support for multiple people using a PDA and its UI updating for all of them and PDA messages being received and responded to from multiple people under the same PDA It also removes pAI camera in favor of using siliconcamera, which is the same thing; this just cuts down on some copy paste. I also reverted PDA's ringer being off preventing messages from being sent to your chat, silent mode was meant to prevent only the ringing sound. ## Why It's Good For The Game pAIs can now do a little bit more to help their owners on a personal level, and adds support for more stuff like this in the future (an idea I had was being able to hack into PDAs in the same way the CE can hack into APCs remotely) This is a re-PR of https://github.com/tgstation/tgstation/pull/76445 but it's a little better this time and does not remove the PDA Messenger app from pAIs. ## Changelog 🆑 fix: PDAs being on silent no longer prevents PDAs from being sent to your chat, again. add: pAIs inserted into a PDA can now control the PDA, and will receive PDA messages sent to it (and can respond under the PDA's name). /🆑 * pAIs control a PDA they're inserted into --------- Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
412 lines
14 KiB
Plaintext
412 lines
14 KiB
Plaintext
/obj/item/modular_computer/pda
|
|
name = "pda"
|
|
icon = 'icons/obj/modular_pda.dmi'
|
|
icon_state = "pda"
|
|
worn_icon_state = "nothing"
|
|
base_icon_state = "tablet"
|
|
greyscale_config = /datum/greyscale_config/tablet
|
|
greyscale_colors = "#999875#a92323"
|
|
|
|
lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
|
|
righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
|
|
inhand_icon_state = "electronic"
|
|
|
|
steel_sheet_cost = 2
|
|
custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT * 3, /datum/material/glass=SMALL_MATERIAL_AMOUNT, /datum/material/plastic=SMALL_MATERIAL_AMOUNT)
|
|
interaction_flags_atom = INTERACT_ATOM_ALLOW_USER_LOCATION | INTERACT_ATOM_IGNORE_MOBILITY
|
|
|
|
icon_state_menu = "menu"
|
|
max_capacity = 64
|
|
allow_chunky = TRUE
|
|
hardware_flag = PROGRAM_TABLET
|
|
max_idle_programs = 2
|
|
w_class = WEIGHT_CLASS_SMALL
|
|
slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT
|
|
has_light = TRUE //LED flashlight!
|
|
comp_light_luminosity = 2.3 //this is what old PDAs were set to
|
|
looping_sound = FALSE
|
|
|
|
///The item currently inserted into the PDA, starts with a pen.
|
|
var/obj/item/inserted_item = /obj/item/pen
|
|
|
|
///Whether the PDA should have 'pda_programs' apps installed on Initialize.
|
|
var/has_pda_programs = TRUE
|
|
///Static list of default PDA apps to install on Initialize.
|
|
var/static/list/datum/computer_file/pda_programs = list(
|
|
/datum/computer_file/program/messenger,
|
|
/datum/computer_file/program/nt_pay,
|
|
/datum/computer_file/program/notepad,
|
|
// SKYRAT EDIT ADDITION START
|
|
/datum/computer_file/program/crew_manifest, // Adds crew manifest to all base tablets
|
|
// SKRAT EDIT ADDITION END
|
|
)
|
|
///List of items that can be stored in a PDA
|
|
var/static/list/contained_item = list(
|
|
/obj/item/pen,
|
|
/obj/item/toy/crayon,
|
|
/obj/item/lipstick,
|
|
/obj/item/flashlight/pen,
|
|
/obj/item/reagent_containers/hypospray/medipen,
|
|
/obj/item/clothing/mask/cigarette,
|
|
)
|
|
|
|
/obj/item/modular_computer/pda/Initialize(mapload)
|
|
. = ..()
|
|
if(inserted_item)
|
|
inserted_item = new inserted_item(src)
|
|
|
|
/obj/item/modular_computer/pda/Destroy()
|
|
if(istype(inserted_item))
|
|
QDEL_NULL(inserted_item)
|
|
return ..()
|
|
|
|
/obj/item/modular_computer/pda/install_default_programs()
|
|
var/list/apps_to_download = list()
|
|
if(has_pda_programs)
|
|
apps_to_download += default_programs + pda_programs
|
|
apps_to_download += starting_programs
|
|
|
|
for(var/programs as anything in apps_to_download)
|
|
var/datum/computer_file/program/program_type = new programs
|
|
store_file(program_type)
|
|
|
|
/obj/item/modular_computer/pda/update_overlays()
|
|
. = ..()
|
|
if(computer_id_slot)
|
|
. += mutable_appearance(initial(icon), "id_overlay")
|
|
if(light_on)
|
|
. += mutable_appearance(initial(icon), "light_overlay")
|
|
if(inserted_pai)
|
|
. += mutable_appearance(initial(icon), "pai_inserted")
|
|
|
|
/obj/item/modular_computer/pda/attack_ai(mob/user)
|
|
to_chat(user, span_notice("It doesn't feel right to snoop around like that..."))
|
|
return // we don't want ais or cyborgs using a private role tablet
|
|
|
|
/obj/item/modular_computer/pda/interact(mob/user)
|
|
. = ..()
|
|
if(HAS_TRAIT(src, TRAIT_PDA_MESSAGE_MENU_RIGGED))
|
|
explode(user, from_message_menu = TRUE)
|
|
|
|
/obj/item/modular_computer/pda/attack_self(mob/user)
|
|
// bypass literacy checks to access syndicate uplink
|
|
var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink)
|
|
if(hidden_uplink?.owner && HAS_TRAIT(user, TRAIT_ILLITERATE))
|
|
if(hidden_uplink.owner != user.key)
|
|
return ..()
|
|
|
|
hidden_uplink.locked = FALSE
|
|
hidden_uplink.interact(null, user)
|
|
return COMPONENT_CANCEL_ATTACK_CHAIN
|
|
|
|
return ..()
|
|
|
|
/obj/item/modular_computer/pda/pre_attack(atom/target, mob/living/user, params)
|
|
if(!inserted_disk || !ismachinery(target))
|
|
return ..()
|
|
|
|
var/obj/machinery/target_machine = target
|
|
if(!target_machine.panel_open && !istype(target, /obj/machinery/computer))
|
|
return ..()
|
|
|
|
if(!istype(inserted_disk, /obj/item/computer_disk/virus/clown))
|
|
return ..()
|
|
var/obj/item/computer_disk/virus/clown/installed_cartridge = inserted_disk
|
|
if(!installed_cartridge.charges)
|
|
to_chat(user, span_notice("Out of virus charges."))
|
|
return ..()
|
|
|
|
to_chat(user, span_notice("You upload the virus to [target]!"))
|
|
var/sig_list = list(COMSIG_ATOM_ATTACK_HAND)
|
|
if(istype(target,/obj/machinery/door/airlock))
|
|
sig_list = list(COMSIG_AIRLOCK_OPEN, COMSIG_AIRLOCK_CLOSE)
|
|
|
|
installed_cartridge.charges--
|
|
target.AddComponent(
|
|
/datum/component/sound_player, \
|
|
uses = rand(15,20), \
|
|
signal_list = sig_list, \
|
|
)
|
|
return TRUE
|
|
|
|
/obj/item/modular_computer/pda/add_context(atom/source, list/context, obj/item/held_item, mob/user)
|
|
. = ..()
|
|
|
|
if(inserted_item)
|
|
context[SCREENTIP_CONTEXT_CTRL_LMB] = "Remove [inserted_item]"
|
|
. = CONTEXTUAL_SCREENTIP_SET
|
|
else if(istype(held_item) && is_type_in_list(held_item, contained_item))
|
|
context[SCREENTIP_CONTEXT_LMB] = "Insert [held_item]"
|
|
. = CONTEXTUAL_SCREENTIP_SET
|
|
|
|
return . || NONE
|
|
|
|
/obj/item/modular_computer/pda/attackby(obj/item/attacking_item, mob/user, params)
|
|
. = ..()
|
|
|
|
if(!is_type_in_list(attacking_item, contained_item))
|
|
return
|
|
if(attacking_item.w_class >= WEIGHT_CLASS_SMALL) // Anything equal to or larger than small won't work
|
|
user.balloon_alert(user, "too big!")
|
|
return
|
|
if(inserted_item)
|
|
balloon_alert(user, "no room!")
|
|
return
|
|
if(!user.transferItemToLoc(attacking_item, src))
|
|
return
|
|
balloon_alert(user, "inserted [attacking_item]")
|
|
inserted_item = attacking_item
|
|
playsound(src, 'sound/machines/pda_button1.ogg', 50, TRUE)
|
|
|
|
/obj/item/modular_computer/pda/AltClick(mob/user)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
|
|
remove_pen(user)
|
|
|
|
/obj/item/modular_computer/pda/CtrlClick(mob/user)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
|
|
remove_pen(user)
|
|
|
|
///Finds how hard it is to send a virus to this tablet, checking all programs downloaded.
|
|
/obj/item/modular_computer/pda/proc/get_detomatix_difficulty()
|
|
var/detomatix_difficulty
|
|
|
|
for(var/datum/computer_file/program/downloaded_apps in stored_files)
|
|
detomatix_difficulty += downloaded_apps.detomatix_resistance
|
|
|
|
return detomatix_difficulty
|
|
|
|
/obj/item/modular_computer/pda/proc/remove_pen(mob/user)
|
|
|
|
if(issilicon(user) || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) //TK doesn't work even with this removed but here for readability
|
|
return
|
|
|
|
if(inserted_item)
|
|
balloon_alert(user, "removed [inserted_item]")
|
|
user.put_in_hands(inserted_item)
|
|
inserted_item = null
|
|
update_appearance()
|
|
playsound(src, 'sound/machines/pda_button2.ogg', 50, TRUE)
|
|
|
|
/obj/item/modular_computer/pda/proc/explode(mob/target, mob/bomber, from_message_menu = FALSE)
|
|
var/turf/current_turf = get_turf(src)
|
|
|
|
if(from_message_menu)
|
|
log_bomber(null, null, target, "'s tablet exploded as [target.p_they()] tried to open their tablet message menu because of a recent tablet bomb.")
|
|
else
|
|
log_bomber(bomber, "successfully tablet-bombed", target, "as [target.p_they()] tried to reply to a rigged tablet message [bomber && !is_special_character(bomber) ? "(SENT BY NON-ANTAG)" : ""]")
|
|
|
|
if (ismob(loc))
|
|
var/mob/loc_mob = loc
|
|
loc_mob.show_message(
|
|
msg = span_userdanger("Your [src] explodes!"),
|
|
type = MSG_VISUAL,
|
|
alt_msg = span_warning("You hear a loud *pop*!"),
|
|
alt_type = MSG_AUDIBLE,
|
|
)
|
|
else
|
|
visible_message(span_danger("[src] explodes!"), span_warning("You hear a loud *pop*!"))
|
|
|
|
target.client?.give_award(/datum/award/achievement/misc/clickbait, target)
|
|
|
|
if(current_turf)
|
|
current_turf.hotspot_expose(700,125)
|
|
if(istype(inserted_disk, /obj/item/computer_disk/virus/detomatix))
|
|
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)
|
|
|
|
|
|
/**
|
|
* A simple helper proc that applies the client's ringtone prefs to the tablet's messenger app,
|
|
* if it has one.
|
|
*
|
|
* Arguments:
|
|
* * owner_client - The client whose prefs we'll use to set the ringtone of this PDA.
|
|
*/
|
|
/obj/item/modular_computer/pda/proc/update_pda_prefs(client/owner_client)
|
|
if(!owner_client)
|
|
return
|
|
|
|
var/new_ringtone = owner_client.prefs.read_preference(/datum/preference/text/pda_ringtone)
|
|
if(new_ringtone && (new_ringtone != MESSENGER_RINGTONE_DEFAULT))
|
|
update_ringtone(new_ringtone)
|
|
|
|
var/new_theme = owner_client.prefs.read_preference(/datum/preference/choiced/pda_theme)
|
|
if(new_theme)
|
|
device_theme = GLOB.pda_name_to_theme[new_theme]
|
|
|
|
/// A simple proc to set the ringtone from a pda.
|
|
/obj/item/modular_computer/pda/proc/update_ringtone(new_ringtone)
|
|
if(!istext(new_ringtone))
|
|
return
|
|
var/datum/computer_file/program/messenger/messenger_app = locate() in stored_files
|
|
if(messenger_app)
|
|
messenger_app.ringtone = new_ringtone
|
|
|
|
/**
|
|
* Nuclear PDA
|
|
*
|
|
* PDA that doesn't come with the default apps but has Fission360
|
|
* Resistant to emags, these are given to nukies for disk pinpointer stuff.
|
|
*/
|
|
/obj/item/modular_computer/pda/nukeops
|
|
name = "nuclear pda"
|
|
device_theme = PDA_THEME_SYNDICATE
|
|
comp_light_luminosity = 6.3 //matching a flashlight
|
|
light_color = COLOR_RED
|
|
greyscale_config = /datum/greyscale_config/tablet/stripe_thick
|
|
greyscale_colors = "#a80001#5C070F#000000"
|
|
long_ranged = TRUE
|
|
starting_programs = list(
|
|
/datum/computer_file/program/radar/fission360,
|
|
)
|
|
|
|
/obj/item/modular_computer/pda/nukeops/Initialize(mapload)
|
|
. = ..()
|
|
emag_act(forced = TRUE)
|
|
var/datum/computer_file/program/messenger/msg = locate() in stored_files
|
|
if(msg)
|
|
msg.invisible = TRUE
|
|
|
|
/**
|
|
* Silicon PDA
|
|
*
|
|
* PDAs that are built-in to Silicons and should not exist at any point without being inside of one.
|
|
*/
|
|
/obj/item/modular_computer/pda/silicon
|
|
name = "modular interface"
|
|
icon_state = "tablet-silicon"
|
|
base_icon_state = "tablet-silicon"
|
|
greyscale_config = null
|
|
greyscale_colors = null
|
|
|
|
has_light = FALSE //tablet light button actually enables/disables the borg lamp
|
|
comp_light_luminosity = 0
|
|
inserted_item = null
|
|
has_pda_programs = FALSE
|
|
starting_programs = list(
|
|
/datum/computer_file/program/messenger,
|
|
)
|
|
|
|
///Ref to the RoboTact app. Important enough to borgs to deserve a ref.
|
|
var/datum/computer_file/program/robotact/robotact
|
|
///IC log that borgs can view in their personal management app
|
|
var/list/borglog = list()
|
|
///Ref to the silicon we're installed in. Set by the silicon itself during its creation.
|
|
var/mob/living/silicon/silicon_owner
|
|
|
|
/obj/item/modular_computer/pda/silicon/cyborg
|
|
starting_programs = list(
|
|
/datum/computer_file/program/filemanager,
|
|
/datum/computer_file/program/robotact,
|
|
/datum/computer_file/program/crew_manifest, // SKYRAT EDIT ADDITION - Manifests for borgs
|
|
/datum/computer_file/program/messenger, // SKYRAT EDIT ADDITION - Messenger for borgs
|
|
)
|
|
|
|
/obj/item/modular_computer/pda/silicon/Initialize(mapload)
|
|
. = ..()
|
|
vis_flags |= VIS_INHERIT_ID
|
|
silicon_owner = loc
|
|
if(!istype(silicon_owner))
|
|
silicon_owner = null
|
|
stack_trace("[type] initialized outside of a silicon, deleting.")
|
|
return INITIALIZE_HINT_QDEL
|
|
|
|
/obj/item/modular_computer/pda/silicon/Destroy()
|
|
silicon_owner = null
|
|
return ..()
|
|
|
|
/obj/item/modular_computer/pda/silicon/turn_on(mob/user, open_ui = FALSE)
|
|
if(silicon_owner?.stat != DEAD)
|
|
return ..()
|
|
return FALSE
|
|
|
|
/obj/item/modular_computer/pda/silicon/get_ntnet_status()
|
|
//No borg found
|
|
if(!silicon_owner)
|
|
return FALSE
|
|
// no AIs/pAIs
|
|
var/mob/living/silicon/robot/cyborg_check = silicon_owner
|
|
if(!istype(cyborg_check))
|
|
return ..()
|
|
//lockdown restricts borg networking
|
|
if(cyborg_check.lockcharge)
|
|
return FALSE
|
|
//borg cell dying restricts borg networking
|
|
if(!cyborg_check.cell || cyborg_check.cell.charge == 0)
|
|
return FALSE
|
|
|
|
return ..()
|
|
|
|
/**
|
|
* Returns a ref to the RoboTact app, creating the app if need be.
|
|
*
|
|
* The RoboTact app is important for borgs, and so should always be available.
|
|
* This proc will look for it in the tablet's robotact var, then check the
|
|
* hard drive if the robotact var is unset, and finally attempt to create a new
|
|
* copy if the hard drive does not contain the app. If the hard drive rejects
|
|
* the new copy (such as due to lack of space), the proc will crash with an error.
|
|
* RoboTact is supposed to be undeletable, so these will create runtime messages.
|
|
*/
|
|
/obj/item/modular_computer/pda/silicon/proc/get_robotact()
|
|
if(robotact)
|
|
return robotact
|
|
robotact = find_file_by_name("robotact")
|
|
if(robotact)
|
|
return robotact
|
|
stack_trace("Cyborg [silicon_owner] ( [silicon_owner.type] ) was somehow missing their self-manage app in their tablet. A new copy has been created.")
|
|
robotact = new(src)
|
|
if(store_file(robotact))
|
|
return robotact
|
|
qdel(robotact)
|
|
robotact = null
|
|
CRASH("Cyborg [silicon_owner]'s tablet hard drive rejected recieving a new copy of the self-manage app. To fix, check the hard drive's space remaining. Please make a bug report about this.")
|
|
|
|
//Makes the light settings reflect the borg's headlamp settings
|
|
/obj/item/modular_computer/pda/silicon/cyborg/ui_data(mob/user)
|
|
. = ..()
|
|
.["has_light"] = TRUE
|
|
if(iscyborg(silicon_owner))
|
|
var/mob/living/silicon/robot/robo = silicon_owner
|
|
.["light_on"] = robo.lamp_enabled
|
|
.["comp_light_color"] = robo.lamp_color
|
|
|
|
//Makes the flashlight button affect the borg rather than the tablet
|
|
/obj/item/modular_computer/pda/silicon/toggle_flashlight()
|
|
if(!silicon_owner || QDELETED(silicon_owner))
|
|
return FALSE
|
|
if(iscyborg(silicon_owner))
|
|
var/mob/living/silicon/robot/robo = silicon_owner
|
|
robo.toggle_headlamp()
|
|
return TRUE
|
|
|
|
//Makes the flashlight color setting affect the borg rather than the tablet
|
|
/obj/item/modular_computer/pda/silicon/set_flashlight_color(color)
|
|
if(!silicon_owner || QDELETED(silicon_owner) || !color)
|
|
return FALSE
|
|
if(iscyborg(silicon_owner))
|
|
var/mob/living/silicon/robot/robo = silicon_owner
|
|
robo.lamp_color = color
|
|
robo.toggle_headlamp(FALSE, TRUE)
|
|
return TRUE
|
|
|
|
/obj/item/modular_computer/pda/silicon/ui_state(mob/user)
|
|
return GLOB.reverse_contained_state
|
|
|
|
/obj/item/modular_computer/pda/silicon/cyborg/syndicate
|
|
icon_state = "tablet-silicon-syndicate"
|
|
device_theme = PDA_THEME_SYNDICATE
|
|
|
|
/obj/item/modular_computer/pda/silicon/cyborg/syndicate/Initialize(mapload)
|
|
. = ..()
|
|
if(iscyborg(silicon_owner))
|
|
var/mob/living/silicon/robot/robo = silicon_owner
|
|
robo.lamp_color = COLOR_RED //Syndicate likes it red
|