mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-12 07:33:34 +01:00
[READY] TGUI pAIs (#14524)
* TGUI pAIs * Nukes an old template * pAI Crew Manifest * pAI Atmos Scan * pAI Medical Records * pAI Security Records + code cleanup * pAI directives + More refactoring * Sorts the toggles out * pAI messenger * WIP - Radios * pAI radio - working * pAI Signalers * Nukes some arleady gone templates * Forgot to purge this too * pAI Door Jack * pAI Bioscan * Mochi tweaks * Farie Fixes * Spacing
This commit is contained in:
@@ -7,12 +7,12 @@
|
||||
silence_time = null
|
||||
to_chat(src, "<font color=green>Communication circuit reinitialized. Speech and messaging functionality restored.</font>")
|
||||
|
||||
if(cable)
|
||||
if(get_dist(src, cable) > 1)
|
||||
var/turf/T = get_turf_or_move(loc)
|
||||
for(var/mob/M in viewers(T))
|
||||
M.show_message("<span class='warning'>The data cable rapidly retracts back into its spool.</span>", 3, "<span class='warning'>You hear a click and the sound of wire spooling rapidly.</span>", 2)
|
||||
QDEL_NULL(cable)
|
||||
if(installed_software["doorjack"])
|
||||
var/datum/pai_software/door_jack/DJ = installed_software["doorjack"]
|
||||
if(DJ.cable)
|
||||
if(get_dist(src, DJ.cable) > 1)
|
||||
visible_message("<span class='warning'>The data cable connected to [src] rapidly retracts back into its spool!</span>")
|
||||
QDEL_NULL(DJ.cable)
|
||||
|
||||
/mob/living/silicon/pai/updatehealth(reason = "none given")
|
||||
if(status_flags & GODMODE)
|
||||
|
||||
@@ -9,11 +9,8 @@
|
||||
pass_flags = PASSTABLE
|
||||
density = 0
|
||||
holder_type = /obj/item/holder/pai
|
||||
var/network = "SS13"
|
||||
var/obj/machinery/camera/current = null
|
||||
|
||||
var/ram = 100 // Used as currency to purchase different abilities
|
||||
var/list/software = list()
|
||||
var/userDNA // The DNA string of our assigned user
|
||||
var/obj/item/paicard/card // The card we inhabit
|
||||
var/obj/item/radio/radio // Our primary radio
|
||||
@@ -42,9 +39,6 @@
|
||||
)
|
||||
|
||||
|
||||
|
||||
var/obj/item/pai_cable/cable // The cable we produce and use when door or camera jacking
|
||||
|
||||
var/master // Name of the one who commands us
|
||||
var/master_dna // DNA string for owner verification
|
||||
// Keeping this separate from the laws var, it should be much more difficult to modify
|
||||
@@ -64,17 +58,11 @@
|
||||
var/secHUD = 0 // Toggles whether the Security HUD is active or not
|
||||
var/medHUD = 0 // Toggles whether the Medical HUD is active or not
|
||||
|
||||
var/medical_cannotfind = 0
|
||||
var/datum/data/record/medicalActive1 // Datacore record declarations for record software
|
||||
var/datum/data/record/medicalActive2
|
||||
/// Currently active software
|
||||
var/datum/pai_software/active_software
|
||||
|
||||
var/security_cannotfind = 0
|
||||
var/datum/data/record/securityActive1 // Could probably just combine all these into one
|
||||
var/datum/data/record/securityActive2
|
||||
|
||||
var/obj/machinery/door/hackdoor // The airlock being hacked
|
||||
var/hackprogress = 0 // Possible values: 0 - 100, >= 100 means the hack is complete and will be reset upon next check
|
||||
var/hack_aborted = 0
|
||||
/// List of all installed software
|
||||
var/list/datum/pai_software/installed_software = list()
|
||||
|
||||
var/obj/item/integrated_radio/signal/sradio // AI's signaller
|
||||
|
||||
@@ -109,20 +97,20 @@
|
||||
|
||||
//PDA
|
||||
pda = new(src)
|
||||
spawn(5)
|
||||
pda.ownjob = "Personal Assistant"
|
||||
pda.owner = text("[]", src)
|
||||
pda.name = pda.owner + " (" + pda.ownjob + ")"
|
||||
var/datum/data/pda/app/messenger/M = pda.find_program(/datum/data/pda/app/messenger)
|
||||
M.toff = 1
|
||||
..()
|
||||
pda.ownjob = "Personal Assistant"
|
||||
pda.owner = "[src]"
|
||||
pda.name = "[pda.owner] ([pda.ownjob])"
|
||||
var/datum/data/pda/app/messenger/M = pda.find_program(/datum/data/pda/app/messenger)
|
||||
M.toff = TRUE
|
||||
|
||||
/mob/living/silicon/pai/Destroy()
|
||||
medicalActive1 = null
|
||||
medicalActive2 = null
|
||||
securityActive1 = null
|
||||
securityActive2 = null
|
||||
return ..()
|
||||
// Software modules. No these var names have nothing to do with photoshop
|
||||
for(var/PS in subtypesof(/datum/pai_software))
|
||||
var/datum/pai_software/PSD = new PS(src)
|
||||
if(PSD.default)
|
||||
installed_software[PSD.id] = PSD
|
||||
|
||||
active_software = installed_software["mainmenu"] // Default us to the main menu
|
||||
..()
|
||||
|
||||
/mob/living/silicon/pai/can_unbuckle()
|
||||
return FALSE
|
||||
@@ -159,12 +147,6 @@
|
||||
for(var/obj/effect/proc_holder/P in proc_holder_list)
|
||||
statpanel("[P.panel]","",P)
|
||||
|
||||
/mob/living/silicon/pai/check_eye(var/mob/user as mob)
|
||||
if(!current)
|
||||
return null
|
||||
user.reset_perspective(current)
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/pai/blob_act()
|
||||
if(stat != DEAD)
|
||||
adjustBruteLoss(60)
|
||||
@@ -229,7 +211,7 @@
|
||||
return
|
||||
|
||||
|
||||
// See software.dm for Topic()
|
||||
// See software.dm for tgui_act()
|
||||
|
||||
/mob/living/silicon/pai/attack_animal(mob/living/simple_animal/M)
|
||||
. = ..()
|
||||
@@ -238,78 +220,6 @@
|
||||
add_attack_logs(M, src, "Animal attacked for [damage] damage")
|
||||
adjustBruteLoss(damage)
|
||||
|
||||
/mob/living/silicon/pai/proc/switchCamera(var/obj/machinery/camera/C)
|
||||
usr:cameraFollow = null
|
||||
if(!C)
|
||||
unset_machine()
|
||||
reset_perspective(null)
|
||||
return 0
|
||||
if(stat == 2 || !C.status || !(network in C.network)) return 0
|
||||
|
||||
// ok, we're alive, camera is good and in our network...
|
||||
|
||||
set_machine(src)
|
||||
src:current = C
|
||||
reset_perspective(C)
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/pai/verb/reset_record_view()
|
||||
set category = "pAI Commands"
|
||||
set name = "Reset Records Software"
|
||||
|
||||
securityActive1 = null
|
||||
securityActive2 = null
|
||||
security_cannotfind = 0
|
||||
medicalActive1 = null
|
||||
medicalActive2 = null
|
||||
medical_cannotfind = 0
|
||||
SSnanoui.update_uis(src)
|
||||
to_chat(usr, "<span class='notice'>You reset your record-viewing software.</span>")
|
||||
|
||||
/mob/living/silicon/pai/cancel_camera()
|
||||
set category = "pAI Commands"
|
||||
set name = "Cancel Camera View"
|
||||
reset_perspective(null)
|
||||
unset_machine()
|
||||
src:cameraFollow = null
|
||||
|
||||
//Addition by Mord_Sith to define AI's network change ability
|
||||
/*
|
||||
/mob/living/silicon/pai/proc/pai_network_change()
|
||||
set category = "pAI Commands"
|
||||
set name = "Change Camera Network"
|
||||
reset_perspective(null)
|
||||
unset_machine()
|
||||
src:cameraFollow = null
|
||||
var/cameralist[0]
|
||||
|
||||
if(usr.stat == 2)
|
||||
to_chat(usr, "You can't change your camera network because you are dead!")
|
||||
return
|
||||
|
||||
for(var/obj/machinery/camera/C in Cameras)
|
||||
if(!C.status)
|
||||
continue
|
||||
else
|
||||
if(C.network != "CREED" && C.network != "thunder" && C.network != "RD" && C.network != "toxins" && C.network != "Prison") COMPILE ERROR! This will have to be updated as camera.network is no longer a string, but a list instead
|
||||
cameralist[C.network] = C.network
|
||||
|
||||
network = input(usr, "Which network would you like to view?") as null|anything in cameralist
|
||||
to_chat(src, "<span class='notice'>Switched to [network] camera network.</span>")
|
||||
//End of code by Mord_Sith
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
// Debug command - Maybe should be added to admin verbs later
|
||||
/mob/verb/makePAI(var/turf/t in view())
|
||||
var/obj/item/paicard/card = new(t)
|
||||
var/mob/living/silicon/pai/pai = new(card)
|
||||
pai.key = key
|
||||
card.setPersonality(pai)
|
||||
|
||||
*/
|
||||
|
||||
// Procs/code after this point is used to convert the stationary pai item into a
|
||||
// mobile pai mob. This also includes handling some of the general shit that can occur
|
||||
// to it. Really this deserves its own file, but for the moment it can sit here. ~ Z
|
||||
|
||||
@@ -10,113 +10,41 @@ GLOBAL_LIST_INIT(pai_emotions, list(
|
||||
"What" = 9
|
||||
))
|
||||
|
||||
|
||||
GLOBAL_LIST_EMPTY(pai_software_by_key)
|
||||
GLOBAL_LIST_EMPTY(default_pai_software)
|
||||
|
||||
/mob/living/silicon/pai/New()
|
||||
..()
|
||||
software = GLOB.default_pai_software.Copy()
|
||||
|
||||
/mob/living/silicon/pai/verb/paiInterface()
|
||||
set category = "pAI Commands"
|
||||
set name = "Software Interface"
|
||||
|
||||
ui_interact(src)
|
||||
tgui_interact(src)
|
||||
|
||||
/mob/living/silicon/pai/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, datum/topic_state/state = GLOB.self_state)
|
||||
if(ui_key != "main")
|
||||
var/datum/pai_software/S = software[ui_key]
|
||||
if(S && !S.toggle)
|
||||
ui = SSnanoui.try_update_ui(user, src, S.id, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, S.id, S.template_file, S.ui_title, S.ui_width, S.ui_height, state = state)
|
||||
ui.open()
|
||||
if(S.autoupdate)
|
||||
ui.set_auto_update(1)
|
||||
else
|
||||
if(ui)
|
||||
ui.set_status(STATUS_CLOSE, 0)
|
||||
return
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
/mob/living/silicon/pai/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_self_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "pai_interface.tmpl", "pAI Software Interface", 450, 600, state = state)
|
||||
ui = new(user, src, ui_key, "PAI", name, 600, 650, master_ui, state)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/mob/living/silicon/pai/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
if(ui_key != "main")
|
||||
var/datum/pai_software/S = software[ui_key]
|
||||
if(S && !S.toggle)
|
||||
return S.on_ui_data(user, state)
|
||||
log_runtime(EXCEPTION("Unrecognized/invalid pAI UI state '[ui_key]'"), src)
|
||||
return
|
||||
// Software we have bought
|
||||
var/bought_software[0]
|
||||
// Software we have not bought
|
||||
var/not_bought_software[0]
|
||||
|
||||
for(var/key in GLOB.pai_software_by_key)
|
||||
var/datum/pai_software/S = GLOB.pai_software_by_key[key]
|
||||
var/software_data[0]
|
||||
software_data["name"] = S.name
|
||||
software_data["id"] = S.id
|
||||
if(key in software)
|
||||
software_data["on"] = S.is_active(src)
|
||||
bought_software[++bought_software.len] = software_data
|
||||
else
|
||||
software_data["ram"] = S.ram_cost
|
||||
not_bought_software[++not_bought_software.len] = software_data
|
||||
|
||||
data["bought"] = bought_software
|
||||
data["not_bought"] = not_bought_software
|
||||
data["available_ram"] = ram
|
||||
|
||||
// Emotions
|
||||
var/emotions[0]
|
||||
for(var/name in GLOB.pai_emotions)
|
||||
var/emote[0]
|
||||
emote["name"] = name
|
||||
emote["id"] = GLOB.pai_emotions[name]
|
||||
emotions[++emotions.len] = emote
|
||||
|
||||
data["emotions"] = emotions
|
||||
data["current_emotion"] = card.current_emotion
|
||||
/mob/living/silicon/pai/tgui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["app_template"] = active_software.template_file
|
||||
data["app_icon"] = active_software.ui_icon
|
||||
data["app_title"] = active_software.name
|
||||
data["app_data"] = active_software.get_app_data(user)
|
||||
|
||||
return data
|
||||
|
||||
/mob/living/silicon/pai/Topic(href, href_list)
|
||||
// Yes the stupid amount of args here is important, so we can proxy stuff to child UIs
|
||||
/mob/living/silicon/pai/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(..())
|
||||
return 1
|
||||
return
|
||||
|
||||
if(href_list["software"])
|
||||
var/soft = href_list["software"]
|
||||
var/datum/pai_software/S = software[soft]
|
||||
if(S.toggle)
|
||||
S.toggle(src)
|
||||
. = TRUE
|
||||
|
||||
switch(action)
|
||||
// This call is global to all templates, hence the prefix
|
||||
if("MASTER_back")
|
||||
active_software = installed_software["mainmenu"]
|
||||
// Bail early
|
||||
return
|
||||
else
|
||||
ui_interact(src, ui_key = soft)
|
||||
return 1
|
||||
|
||||
else if(href_list["stopic"])
|
||||
var/soft = href_list["stopic"]
|
||||
var/datum/pai_software/S = software[soft]
|
||||
if(S)
|
||||
return S.Topic(href, href_list)
|
||||
|
||||
else if(href_list["purchase"])
|
||||
var/soft = href_list["purchase"]
|
||||
var/datum/pai_software/S = GLOB.pai_software_by_key[soft]
|
||||
if(S && (ram >= S.ram_cost))
|
||||
ram -= S.ram_cost
|
||||
software[S.id] = S
|
||||
return 1
|
||||
|
||||
else if(href_list["image"])
|
||||
var/img = text2num(href_list["image"])
|
||||
if(1 <= img && img <= 9)
|
||||
card.setEmotion(img)
|
||||
return 1
|
||||
active_software.tgui_act(action, params, ui, state)
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* # pAI Software
|
||||
*
|
||||
* Datum module for pAI software
|
||||
*
|
||||
* Very similar to the PDA app datum, this determines what UI sub-template to use,
|
||||
* as well as the RAM cost, and if it is toggle software (not a UI app)
|
||||
*
|
||||
*/
|
||||
/datum/pai_software
|
||||
/// Name for the software. This is used as the button text when buying or opening/toggling the software
|
||||
var/name = "pAI software module"
|
||||
/// RAM cost; pAIs start with 100 RAM, spending it on programs
|
||||
var/ram_cost = 0
|
||||
/// ID for the software. This must be unique
|
||||
var/id
|
||||
// Toggled software should override toggle() and is_active()
|
||||
// Non-toggled software should override get_app_data() and Topic()
|
||||
/// Whether this software is a toggle or not
|
||||
var/toggle_software = FALSE
|
||||
/// Do we have this software installed by default
|
||||
var/default = FALSE
|
||||
/// Template for the TGUI file
|
||||
var/template_file = "oops"
|
||||
/// Icon for inside the UI
|
||||
var/ui_icon = "file-code"
|
||||
/// pAI which holds this software
|
||||
var/mob/living/silicon/pai/pai_holder
|
||||
|
||||
/**
|
||||
* New handler
|
||||
*
|
||||
* Ensures that the pai_holder var is set to the pAI itself
|
||||
* Arguments:
|
||||
* * user - The pAI that this softawre is held by
|
||||
*/
|
||||
/datum/pai_software/New(mob/living/silicon/pai/user)
|
||||
pai_holder = user
|
||||
..()
|
||||
|
||||
/**
|
||||
* Handler for the app's UI data
|
||||
*
|
||||
* This returns the list of the current app's data for the UI
|
||||
* This will then be injected as a variable on the TGUI data called "app_data"
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The pAI that is using this app
|
||||
*/
|
||||
/datum/pai_software/proc/get_app_data(mob/living/silicon/pai/user)
|
||||
return list()
|
||||
|
||||
/**
|
||||
* Handler for toggling toggle apps on and off
|
||||
*
|
||||
* This is invoked whenever you toggle a toggleable function
|
||||
* Put your toggleable work in here
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The pAI that is using this toggle
|
||||
*/
|
||||
/datum/pai_software/proc/toggle(mob/living/silicon/pai/user)
|
||||
return
|
||||
|
||||
/**
|
||||
* Helper for checking if a toggle is enabled or not
|
||||
*
|
||||
* Returns TRUE if the toggle software is active, FALSE if not
|
||||
*
|
||||
* Its like this instead of a simple `is_toggled` var because some toggles override eachother and this is easier
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The pAI that is using this app
|
||||
*/
|
||||
/datum/pai_software/proc/is_active(mob/living/silicon/pai/user)
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* Helper proc so that pAIs can get the mob holding them
|
||||
*
|
||||
* This needs to exist because pAIs have many different locs
|
||||
* (Held card, mob itself, in pocket, etc)
|
||||
*
|
||||
* Arguments:
|
||||
* * inform - Boolean, should we inform the pAI if they fail to find a carrier
|
||||
*/
|
||||
/datum/pai_software/proc/get_holding_mob(inform = FALSE)
|
||||
var/mob/living/M = pai_holder.loc
|
||||
var/count = 0
|
||||
|
||||
// Find the carrier
|
||||
while(!istype(M, /mob/living))
|
||||
if(!M || !M.loc || count > 6)
|
||||
//For a runtime where M ends up in nullspace (similar to bluespace but less colourful)
|
||||
if(inform)
|
||||
to_chat(usr, "<span class='warning'>You are not being carried by anyone!</span>")
|
||||
return null
|
||||
M = M.loc
|
||||
count++
|
||||
|
||||
return M
|
||||
|
||||
/**
|
||||
* tgui_act sanity check helper
|
||||
*
|
||||
* Basically checks the existing href exploit stuff, as well as making sure the user using the UI is the pAI itself
|
||||
*/
|
||||
/datum/pai_software/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(..())
|
||||
return TRUE
|
||||
|
||||
if(usr != pai_holder)
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
@@ -0,0 +1,388 @@
|
||||
// Main Menu //
|
||||
/datum/pai_software/main_menu
|
||||
name = "Main Menu"
|
||||
id = "mainmenu"
|
||||
default = TRUE
|
||||
template_file = "pai_main_menu"
|
||||
ui_icon = "home"
|
||||
|
||||
/datum/pai_software/main_menu/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
data["available_ram"] = user.ram
|
||||
|
||||
// Emotions
|
||||
var/list/emotions = list()
|
||||
for(var/name in GLOB.pai_emotions)
|
||||
var/list/emote = list()
|
||||
emote["name"] = name
|
||||
emote["id"] = GLOB.pai_emotions[name]
|
||||
emotions[++emotions.len] = emote
|
||||
|
||||
data["emotions"] = emotions
|
||||
data["current_emotion"] = user.card.current_emotion
|
||||
|
||||
var/list/available_s = list()
|
||||
for(var/s in GLOB.pai_software_by_key)
|
||||
var/datum/pai_software/PS = GLOB.pai_software_by_key[s]
|
||||
available_s += list(list("name" = PS.name, "key" = PS.id, "icon" = PS.ui_icon, "cost" = PS.ram_cost))
|
||||
|
||||
// Split to installed software and toggles for the UI
|
||||
var/list/installed_s = list()
|
||||
var/list/installed_t = list()
|
||||
for(var/s in pai_holder.installed_software)
|
||||
var/datum/pai_software/PS = pai_holder.installed_software[s]
|
||||
if(PS.toggle_software)
|
||||
installed_t += list(list("name" = PS.name, "key" = PS.id, "icon" = PS.ui_icon, "active" = PS.is_active(user)))
|
||||
else
|
||||
installed_s += list(list("name" = PS.name, "key" = PS.id, "icon" = PS.ui_icon))
|
||||
|
||||
data["available_software"] = available_s
|
||||
data["installed_software"] = installed_s
|
||||
data["installed_toggles"] = installed_t
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/main_menu/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("purchaseSoftware")
|
||||
var/datum/pai_software/S = GLOB.pai_software_by_key[params["key"]]
|
||||
if(S && (pai_holder.ram >= S.ram_cost))
|
||||
var/datum/pai_software/newPS = new S.type(pai_holder)
|
||||
pai_holder.ram -= newPS.ram_cost
|
||||
pai_holder.installed_software[newPS.id] = newPS
|
||||
if("setEmotion")
|
||||
var/emotion = clamp(text2num(params["emotion"]), 1, 9)
|
||||
pai_holder.card.setEmotion(emotion)
|
||||
if("startSoftware")
|
||||
var/software_key = params["software_key"]
|
||||
if(pai_holder.installed_software[software_key])
|
||||
pai_holder.active_software = pai_holder.installed_software[software_key]
|
||||
if("setToggle")
|
||||
var/toggle_key = params["toggle_key"]
|
||||
if(pai_holder.installed_software[toggle_key])
|
||||
pai_holder.installed_software[toggle_key].toggle(pai_holder)
|
||||
|
||||
// Directives //
|
||||
/datum/pai_software/directives
|
||||
name = "Directives"
|
||||
id = "directives"
|
||||
default = TRUE
|
||||
template_file = "pai_directives"
|
||||
ui_icon = "clipboard-list"
|
||||
|
||||
/datum/pai_software/directives/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["master"] = user.master
|
||||
data["dna"] = user.master_dna
|
||||
data["prime"] = user.pai_law0
|
||||
data["supplemental"] = user.pai_laws
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/directives/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
. = TRUE
|
||||
|
||||
switch(action)
|
||||
if("getdna")
|
||||
var/mob/living/M = get_holding_mob()
|
||||
if(!istype(M))
|
||||
return
|
||||
|
||||
// Check the carrier
|
||||
var/answer = alert(M, "[pai_holder] is requesting a DNA sample from you. Will you allow it to confirm your identity?", "[pai_holder] Check DNA", "Yes", "No")
|
||||
if(answer == "Yes")
|
||||
M.visible_message("<span class='notice'>[M] presses [M.p_their()] thumb against [pai_holder].</span>", "<span class='notice'>You press your thumb against [pai_holder].</span>")
|
||||
var/datum/dna/dna = M.dna
|
||||
to_chat(usr, "<span class='notice'>[M]'s UE string: [dna.unique_enzymes]</span>")
|
||||
if(dna.unique_enzymes == pai_holder.master_dna)
|
||||
to_chat(usr, "<span class='notice'>DNA is a match to stored Master DNA.</span>")
|
||||
else
|
||||
to_chat(usr, "<span class='warning'>DNA does not match stored Master DNA.</span>")
|
||||
else
|
||||
to_chat(usr, "<span class='warning'>[M] does not seem like [M.p_they()] [M.p_are()] going to provide a DNA sample willingly.</span>")
|
||||
|
||||
// Crew Manifest //
|
||||
/datum/pai_software/crew_manifest
|
||||
name = "Crew Manifest"
|
||||
ram_cost = 5
|
||||
id = "manifest"
|
||||
template_file = "pai_manifest"
|
||||
ui_icon = "users"
|
||||
|
||||
/datum/pai_software/crew_manifest/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
|
||||
GLOB.data_core.get_manifest_json()
|
||||
data["manifest"] = GLOB.PDA_Manifest
|
||||
|
||||
return data
|
||||
|
||||
// Med Records //
|
||||
/datum/pai_software/med_records
|
||||
name = "Medical Records"
|
||||
ram_cost = 15
|
||||
id = "med_records"
|
||||
template_file = "pai_medrecords"
|
||||
ui_icon = "heartbeat"
|
||||
/// Integrated medical records module to reduce duplicated code
|
||||
var/datum/data/pda/app/crew_records/medical/integrated_records = new
|
||||
|
||||
/datum/pai_software/med_records/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
// Just grab the stuff internally
|
||||
integrated_records.update_ui(user, data)
|
||||
return data
|
||||
|
||||
/datum/pai_software/med_records/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(..())
|
||||
return
|
||||
// Double proxy here
|
||||
integrated_records.tgui_act(action, params, ui, state)
|
||||
|
||||
// Sec Records //
|
||||
/datum/pai_software/sec_records
|
||||
name = "Security Records"
|
||||
ram_cost = 15
|
||||
id = "sec_records"
|
||||
template_file = "pai_secrecords"
|
||||
ui_icon = "id-badge"
|
||||
/// Integrated security records module to reduce duplicated code
|
||||
var/datum/data/pda/app/crew_records/security/integrated_records = new
|
||||
|
||||
/datum/pai_software/sec_records/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
// Just grab the stuff internally
|
||||
integrated_records.update_ui(user, data)
|
||||
return data
|
||||
|
||||
/datum/pai_software/sec_records/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(..())
|
||||
return
|
||||
// Double proxy here
|
||||
integrated_records.tgui_act(action, params, ui, state)
|
||||
|
||||
// Atmos Scan //
|
||||
/datum/pai_software/atmosphere_sensor
|
||||
name = "Atmosphere Sensor"
|
||||
ram_cost = 5
|
||||
id = "atmos_sense"
|
||||
template_file = "pai_atmosphere"
|
||||
ui_icon = "fire"
|
||||
/// Integrated PDA atmos scan module to reduce duplicated code
|
||||
var/datum/data/pda/app/atmos_scanner/scanner = new
|
||||
|
||||
/datum/pai_software/atmosphere_sensor/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
// Just grab the stuff internally
|
||||
scanner.update_ui(user, data)
|
||||
return data
|
||||
|
||||
// Messenger //
|
||||
/datum/pai_software/messenger
|
||||
name = "Digital Messenger"
|
||||
ram_cost = 5
|
||||
id = "messenger"
|
||||
template_file = "pai_messenger"
|
||||
ui_icon = "envelope"
|
||||
|
||||
/datum/pai_software/messenger/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
|
||||
// Some safety checks
|
||||
if(!user.pda)
|
||||
CRASH("pAI found without PDA.")
|
||||
|
||||
var/datum/data/pda/app/messenger/PM = user.pda.find_program(/datum/data/pda/app/messenger)
|
||||
if(!PM)
|
||||
CRASH("pAI PDA lacks a messenger program")
|
||||
|
||||
// Grab the internal data
|
||||
PM.update_ui(user, data)
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/messenger/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(..())
|
||||
return
|
||||
|
||||
// Grab their messenger
|
||||
var/datum/data/pda/app/messenger/PM = pai_holder.pda.find_program(/datum/data/pda/app/messenger)
|
||||
// Double proxy here
|
||||
PM.tgui_act(action, params, ui, state)
|
||||
|
||||
// Radio
|
||||
/datum/pai_software/radio_config
|
||||
name = "Radio Configuration"
|
||||
id = "radio"
|
||||
default = TRUE
|
||||
template_file = "pai_radio"
|
||||
ui_icon = "broadcast-tower"
|
||||
|
||||
/datum/pai_software/radio_config/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
data["frequency"] = user.radio.frequency
|
||||
data["minFrequency"] = PUBLIC_LOW_FREQ
|
||||
data["maxFrequency"] = PUBLIC_HIGH_FREQ
|
||||
data["broadcasting"] = user.radio.broadcasting
|
||||
return data
|
||||
|
||||
/datum/pai_software/radio_config/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("toggleBroadcast")
|
||||
// Just toggle it
|
||||
pai_holder.radio.broadcasting = !pai_holder.radio.broadcasting
|
||||
|
||||
if("freq")
|
||||
var/new_frequency = sanitize_frequency(text2num(params["freq"]) * 10)
|
||||
pai_holder.radio.set_frequency(new_frequency)
|
||||
|
||||
// Signaler //
|
||||
/datum/pai_software/signaler
|
||||
name = "Remote Signaler"
|
||||
ram_cost = 5
|
||||
id = "signaler"
|
||||
template_file = "pai_signaler"
|
||||
ui_icon = "rss"
|
||||
|
||||
/datum/pai_software/signaler/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["frequency"] = user.sradio.frequency
|
||||
data["code"] = user.sradio.code
|
||||
data["minFrequency"] = PUBLIC_LOW_FREQ
|
||||
data["maxFrequency"] = PUBLIC_HIGH_FREQ
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/signaler/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("signal")
|
||||
pai_holder.sradio.send_signal("ACTIVATE")
|
||||
|
||||
if("freq")
|
||||
var/new_frequency = sanitize_frequency(text2num(params["freq"]) * 10)
|
||||
pai_holder.sradio.set_frequency(new_frequency)
|
||||
|
||||
if("code")
|
||||
pai_holder.sradio.code = clamp(text2num(params["code"]), 1, 100)
|
||||
|
||||
// Door Jack //
|
||||
/datum/pai_software/door_jack
|
||||
name = "Door Jack"
|
||||
ram_cost = 30
|
||||
id = "door_jack"
|
||||
template_file = "pai_doorjack"
|
||||
ui_icon = "door-open"
|
||||
/// Progress on hacking the door
|
||||
var/progress = 0
|
||||
/// Are we hacking?
|
||||
var/hacking = FALSE
|
||||
/// The cable being plugged into a door
|
||||
var/obj/item/pai_cable/cable
|
||||
/// The door being hacked
|
||||
var/obj/machinery/door/hackdoor
|
||||
|
||||
/datum/pai_software/door_jack/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["cable"] = (cable != null)
|
||||
data["machine"] = (cable?.machine != null)
|
||||
data["inprogress"] = (hackdoor != null)
|
||||
data["progress"] = progress
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/door_jack/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("jack")
|
||||
if(cable && cable.machine)
|
||||
hackdoor = cable.machine
|
||||
if(hacking)
|
||||
to_chat(usr, "<span class='warning'>You are already hacking that door!</span>")
|
||||
else
|
||||
hacking = TRUE
|
||||
INVOKE_ASYNC(src, /datum/pai_software/door_jack/.proc/hackloop)
|
||||
if("cancel")
|
||||
hackdoor = null
|
||||
if("cable")
|
||||
if(cable)
|
||||
to_chat(usr, "<span class='warning'>You already have a cable deployed!</span>")
|
||||
return
|
||||
var/turf/T = get_turf(pai_holder)
|
||||
cable = new /obj/item/pai_cable(T)
|
||||
pai_holder.visible_message("<span class='warning'>A port on [pai_holder] opens to reveal [cable], which promptly falls to the floor.</span>")
|
||||
|
||||
/**
|
||||
* Door jack hack loop
|
||||
*
|
||||
* Self-contained proc for handling the hacking of a door.
|
||||
* Invoked asyncly, but will only allow one instance at a time
|
||||
*/
|
||||
/datum/pai_software/door_jack/proc/hackloop()
|
||||
var/obj/machinery/door/D = cable.machine
|
||||
if(!istype(D))
|
||||
cleanup_hack()
|
||||
return
|
||||
while(progress < 100)
|
||||
if(cable && cable.machine == D && cable.machine == hackdoor && get_dist(pai_holder, hackdoor) <= 1)
|
||||
progress = min(progress + rand(1, 20), 100)
|
||||
else
|
||||
cleanup_hack()
|
||||
return
|
||||
if(progress >= 100)
|
||||
D.open()
|
||||
cleanup_hack()
|
||||
return
|
||||
sleep(1 SECONDS) // Update every second
|
||||
|
||||
/**
|
||||
* Door jack cleanup proc
|
||||
*
|
||||
* Self-contained proc for cleaning up failed hack attempts
|
||||
*/
|
||||
/datum/pai_software/door_jack/proc/cleanup_hack()
|
||||
progress = 0
|
||||
hackdoor = null
|
||||
cable.machine = null
|
||||
QDEL_NULL(cable)
|
||||
hacking = FALSE
|
||||
|
||||
// Host Bioscan //
|
||||
/datum/pai_software/host_scan
|
||||
name = "Host Bioscan"
|
||||
ram_cost = 5
|
||||
id = "bioscan"
|
||||
template_file = "pai_bioscan"
|
||||
ui_icon = "heartbeat"
|
||||
|
||||
/datum/pai_software/host_scan/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
|
||||
var/mob/living/held = get_holding_mob(FALSE)
|
||||
|
||||
if(isliving(held))
|
||||
data["holder"] = held.name
|
||||
data["dead"] = (held.stat > UNCONSCIOUS)
|
||||
data["health"] = held.health
|
||||
data["brute"] = held.getBruteLoss()
|
||||
data["oxy"] = held.getOxyLoss()
|
||||
data["tox"] = held.getToxLoss()
|
||||
data["burn"] = held.getFireLoss()
|
||||
|
||||
return data
|
||||
@@ -0,0 +1,95 @@
|
||||
// Security Hud //
|
||||
/datum/pai_software/sec_hud
|
||||
name = "Security HUD"
|
||||
ram_cost = 20
|
||||
id = "sec_hud"
|
||||
ui_icon = "exclamation-triangle"
|
||||
toggle_software = TRUE
|
||||
|
||||
/datum/pai_software/sec_hud/toggle(mob/living/silicon/pai/user)
|
||||
user.secHUD = !user.secHUD
|
||||
user.remove_med_sec_hud()
|
||||
if(user.secHUD)
|
||||
user.add_sec_hud()
|
||||
|
||||
/datum/pai_software/sec_hud/is_active(mob/living/silicon/pai/user)
|
||||
return user.secHUD
|
||||
|
||||
// Medical Hud //
|
||||
/datum/pai_software/med_hud
|
||||
name = "Medical HUD"
|
||||
ram_cost = 20
|
||||
id = "med_hud"
|
||||
ui_icon = "first-aid"
|
||||
toggle_software = TRUE
|
||||
|
||||
/datum/pai_software/med_hud/toggle(mob/living/silicon/pai/user)
|
||||
user.medHUD = !user.medHUD
|
||||
user.remove_med_sec_hud()
|
||||
if(user.medHUD)
|
||||
user.add_med_hud()
|
||||
|
||||
/datum/pai_software/med_hud/is_active(mob/living/silicon/pai/user)
|
||||
return user.medHUD
|
||||
|
||||
// Universal Translator //
|
||||
/datum/pai_software/translator
|
||||
name = "Universal Translator"
|
||||
ram_cost = 35
|
||||
id = "translator"
|
||||
ui_icon = "language"
|
||||
toggle_software = TRUE
|
||||
|
||||
/datum/pai_software/translator/toggle(mob/living/silicon/pai/user)
|
||||
// Galactic Common, Sol Common, Tradeband, Gutter and Trinary are added with New() and are therefore the current default, always active languages
|
||||
user.translator_on = !user.translator_on
|
||||
if(user.translator_on)
|
||||
user.add_language("Sinta'unathi")
|
||||
user.add_language("Siik'tajr")
|
||||
user.add_language("Canilunzt")
|
||||
user.add_language("Skrellian")
|
||||
user.add_language("Vox-pidgin")
|
||||
user.add_language("Rootspeak")
|
||||
user.add_language("Chittin")
|
||||
user.add_language("Bubblish")
|
||||
user.add_language("Orluum")
|
||||
user.add_language("Clownish")
|
||||
user.add_language("Neo-Russkiya")
|
||||
else
|
||||
user.remove_language("Sinta'unathi")
|
||||
user.remove_language("Siik'tajr")
|
||||
user.remove_language("Canilunzt")
|
||||
user.remove_language("Skrellian")
|
||||
user.remove_language("Vox-pidgin")
|
||||
user.remove_language("Rootspeak")
|
||||
user.remove_language("Chittin")
|
||||
user.remove_language("Bubblish")
|
||||
user.remove_language("Orluum")
|
||||
user.remove_language("Clownish")
|
||||
user.remove_language("Neo-Russkiya")
|
||||
|
||||
/datum/pai_software/translator/is_active(mob/living/silicon/pai/user)
|
||||
return user.translator_on
|
||||
|
||||
// FLashlight //
|
||||
/datum/pai_software/flashlight
|
||||
name = "Flashlight"
|
||||
ram_cost = 5
|
||||
id = "flashlight"
|
||||
ui_icon = "lightbulb"
|
||||
toggle_software = TRUE
|
||||
|
||||
/datum/pai_software/flashlight/toggle(mob/living/silicon/pai/user)
|
||||
var/atom/movable/actual_location = istype(user.loc, /obj/item/paicard) ? user.loc : user
|
||||
if(!user.flashlight_on)
|
||||
actual_location.set_light(2)
|
||||
user.card.set_light(2)
|
||||
else
|
||||
actual_location.set_light(0)
|
||||
user.card.set_light(0)
|
||||
|
||||
user.flashlight_on = !user.flashlight_on
|
||||
|
||||
/datum/pai_software/flashlight/is_active(mob/living/silicon/pai/user)
|
||||
return user.flashlight_on
|
||||
|
||||
@@ -1,624 +0,0 @@
|
||||
/datum/pai_software
|
||||
// Name for the software. This is used as the button text when buying or opening/toggling the software
|
||||
var/name = "pAI software module"
|
||||
// RAM cost; pAIs start with 100 RAM, spending it on programs
|
||||
var/ram_cost = 0
|
||||
// ID for the software. This must be unique
|
||||
var/id = ""
|
||||
// Whether this software is a toggle or not
|
||||
// Toggled software should override toggle() and is_active()
|
||||
// Non-toggled software should override on_ui_data() and Topic()
|
||||
var/toggle = 1
|
||||
// Whether pAIs should automatically receive this module at no cost
|
||||
var/default = 0
|
||||
|
||||
// Vars for pAI nanoUI handling
|
||||
var/autoupdate = 0
|
||||
var/template_file = "oops"
|
||||
var/ui_title = "somebody forgot to set this"
|
||||
var/ui_width = 450
|
||||
var/ui_height = 600
|
||||
|
||||
/datum/pai_software/proc/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
return list()
|
||||
|
||||
/datum/pai_software/proc/toggle(mob/living/silicon/pai/user)
|
||||
return
|
||||
|
||||
/datum/pai_software/proc/is_active(mob/living/silicon/pai/user)
|
||||
return 0
|
||||
|
||||
/datum/pai_software/directives
|
||||
name = "Directives"
|
||||
ram_cost = 0
|
||||
id = "directives"
|
||||
toggle = 0
|
||||
default = 1
|
||||
|
||||
template_file = "pai_directives.tmpl"
|
||||
ui_title = "pAI Directives"
|
||||
autoupdate = 1
|
||||
|
||||
/datum/pai_software/directives/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
data["master"] = user.master
|
||||
data["dna"] = user.master_dna
|
||||
data["prime"] = user.pai_law0
|
||||
data["supplemental"] = user.pai_laws
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/directives/Topic(href, href_list)
|
||||
var/mob/living/silicon/pai/P = usr
|
||||
if(!istype(P)) return
|
||||
|
||||
if(href_list["getdna"])
|
||||
var/mob/living/M = P.loc
|
||||
var/count = 0
|
||||
|
||||
// Find the carrier
|
||||
while(!istype(M, /mob/living))
|
||||
if(!M || !M.loc || count > 6)
|
||||
//For a runtime where M ends up in nullspace (similar to bluespace but less colourful)
|
||||
to_chat(P, "You are not being carried by anyone!")
|
||||
return 0
|
||||
M = M.loc
|
||||
count++
|
||||
|
||||
// Check the carrier
|
||||
var/answer = input(M, "[P] is requesting a DNA sample from you. Will you allow it to confirm your identity?", "[P] Check DNA", "No") in list("Yes", "No")
|
||||
if(answer == "Yes")
|
||||
var/turf/T = get_turf_or_move(P.loc)
|
||||
for(var/mob/v in viewers(T))
|
||||
v.show_message("<span class='notice'>[M] presses [M.p_their()] thumb against [P].</span>", 3, "<span class='notice'>[P] makes a sharp clicking sound as it extracts DNA material from [M].</span>", 2)
|
||||
var/datum/dna/dna = M.dna
|
||||
to_chat(P, "<font color = red><h3>[M]'s UE string : [dna.unique_enzymes]</h3></font>")
|
||||
if(dna.unique_enzymes == P.master_dna)
|
||||
to_chat(P, "<b>DNA is a match to stored Master DNA.</b>")
|
||||
else
|
||||
to_chat(P, "<b>DNA does not match stored Master DNA.</b>")
|
||||
else
|
||||
to_chat(P, "[M] does not seem like [M.p_they()] [M.p_are()] going to provide a DNA sample willingly.")
|
||||
return 1
|
||||
|
||||
/datum/pai_software/radio_config
|
||||
name = "Radio Configuration"
|
||||
ram_cost = 0
|
||||
id = "radio"
|
||||
toggle = 0
|
||||
default = 1
|
||||
|
||||
template_file = "pai_radio.tmpl"
|
||||
ui_title = "Radio Configuration"
|
||||
ui_width = 300
|
||||
ui_height = 150
|
||||
|
||||
/datum/pai_software/radio_config/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
data["listening"] = user.radio.broadcasting
|
||||
data["frequency"] = format_frequency(user.radio.frequency)
|
||||
var/channels[0]
|
||||
for(var/ch_name in user.radio.channels)
|
||||
var/ch_stat = user.radio.channels[ch_name]
|
||||
var/ch_dat[0]
|
||||
ch_dat["name"] = ch_name
|
||||
// FREQ_LISTENING is const in /obj/item/radio
|
||||
ch_dat["listening"] = !!(ch_stat & user.radio.FREQ_LISTENING)
|
||||
channels[++channels.len] = ch_dat
|
||||
|
||||
data["channels"] = channels
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/radio_config/Topic(href, href_list)
|
||||
var/mob/living/silicon/pai/P = usr
|
||||
if(!istype(P)) return
|
||||
|
||||
P.radio.Topic(href, href_list)
|
||||
return 1
|
||||
|
||||
/datum/pai_software/crew_manifest
|
||||
name = "Crew Manifest"
|
||||
ram_cost = 5
|
||||
id = "manifest"
|
||||
toggle = 0
|
||||
|
||||
autoupdate = 1
|
||||
template_file = "pai_manifest.tmpl"
|
||||
ui_title = "Crew Manifest"
|
||||
|
||||
/datum/pai_software/crew_manifest/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
GLOB.data_core.get_manifest_json()
|
||||
data["manifest"] = GLOB.PDA_Manifest
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/messenger
|
||||
name = "Digital Messenger"
|
||||
ram_cost = 5
|
||||
id = "messenger"
|
||||
toggle = 0
|
||||
|
||||
autoupdate = 1
|
||||
template_file = "pai_messenger.tmpl"
|
||||
ui_title = "Digital Messenger"
|
||||
|
||||
/datum/pai_software/messenger/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
if(!user.pda)
|
||||
log_runtime(EXCEPTION("pAI found without PDA."), user)
|
||||
return data
|
||||
var/datum/data/pda/app/messenger/M = user.pda.find_program(/datum/data/pda/app/messenger)
|
||||
if(!M)
|
||||
log_runtime(EXCEPTION("pAI PDA lacks a messenger program"), user)
|
||||
return data
|
||||
|
||||
data["receiver_off"] = M.toff
|
||||
data["ringer_off"] = M.notify_silent
|
||||
data["current_ref"] = null
|
||||
data["current_name"] = user.current_pda_messaging
|
||||
|
||||
var/pdas[0]
|
||||
if(!M.toff)
|
||||
for(var/obj/item/pda/P in GLOB.PDAs)
|
||||
var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger)
|
||||
|
||||
if(P == user.pda || !PM || !PM.can_receive())
|
||||
continue
|
||||
var/pda[0]
|
||||
pda["name"] = "[P]"
|
||||
pda["owner"] = "[P.owner]"
|
||||
pda["ref"] = "\ref[P]"
|
||||
if(P.owner == user.current_pda_messaging)
|
||||
data["current_ref"] = "\ref[P]"
|
||||
pdas[++pdas.len] = pda
|
||||
|
||||
data["pdas"] = pdas
|
||||
|
||||
var/messages[0]
|
||||
if(user.current_pda_messaging)
|
||||
for(var/index in M.tnote)
|
||||
if(index["owner"] != user.current_pda_messaging)
|
||||
continue
|
||||
var/msg[0]
|
||||
var/sent = index["sent"]
|
||||
msg["sent"] = sent ? 1 : 0
|
||||
msg["target"] = index["owner"]
|
||||
msg["message"] = index["message"]
|
||||
messages[++messages.len] = msg
|
||||
|
||||
data["messages"] = messages
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/messenger/Topic(href, href_list)
|
||||
var/mob/living/silicon/pai/P = usr
|
||||
if(!istype(P)) return
|
||||
|
||||
if(!isnull(P.pda))
|
||||
var/datum/data/pda/app/messenger/M = P.pda.find_program(/datum/data/pda/app/messenger)
|
||||
if(!M)
|
||||
return
|
||||
|
||||
if(href_list["toggler"])
|
||||
M.toff = href_list["toggler"] != "1"
|
||||
return 1
|
||||
else if(href_list["ringer"])
|
||||
M.notify_silent = href_list["ringer"] != "1"
|
||||
return 1
|
||||
else if(href_list["select"])
|
||||
var/s = href_list["select"]
|
||||
if(s == "*NONE*")
|
||||
P.current_pda_messaging = null
|
||||
else
|
||||
P.current_pda_messaging = s
|
||||
return 1
|
||||
else if(href_list["target"])
|
||||
if(P.silence_time)
|
||||
return alert("Communications circuits remain uninitialized.")
|
||||
|
||||
var/target = locate(href_list["target"])
|
||||
M.create_message(P, target, 1)
|
||||
return 1
|
||||
|
||||
/datum/pai_software/med_records
|
||||
name = "Medical Records"
|
||||
ram_cost = 15
|
||||
id = "med_records"
|
||||
toggle = 0
|
||||
|
||||
autoupdate = 1
|
||||
template_file = "pai_medrecords.tmpl"
|
||||
ui_title = "Medical Records"
|
||||
|
||||
|
||||
/datum/pai_software/med_records/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
var/records[0]
|
||||
for(var/datum/data/record/general in sortRecord(GLOB.data_core.general))
|
||||
var/record[0]
|
||||
record["name"] = general.fields["name"]
|
||||
record["ref"] = "\ref[general]"
|
||||
records[++records.len] = record
|
||||
|
||||
data["records"] = records
|
||||
|
||||
var/datum/data/record/G = user.medicalActive1
|
||||
var/datum/data/record/M = user.medicalActive2
|
||||
data["general"] = G ? G.fields : null
|
||||
data["medical"] = M ? M.fields : null
|
||||
data["could_not_find"] = user.medical_cannotfind
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/med_records/Topic(href, href_list)
|
||||
var/mob/living/silicon/pai/P = usr
|
||||
if(!istype(P)) return
|
||||
|
||||
if(href_list["select"])
|
||||
var/datum/data/record/record = locate(href_list["select"])
|
||||
if(record)
|
||||
var/datum/data/record/R = record
|
||||
var/datum/data/record/M = null
|
||||
if(!( GLOB.data_core.general.Find(R) ))
|
||||
P.medical_cannotfind = 1
|
||||
else
|
||||
P.medical_cannotfind = 0
|
||||
for(var/datum/data/record/E in GLOB.data_core.medical)
|
||||
if((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"]))
|
||||
M = E
|
||||
P.medicalActive1 = R
|
||||
P.medicalActive2 = M
|
||||
else
|
||||
P.medical_cannotfind = 1
|
||||
return 1
|
||||
|
||||
/datum/pai_software/sec_records
|
||||
name = "Security Records"
|
||||
ram_cost = 15
|
||||
id = "sec_records"
|
||||
toggle = 0
|
||||
|
||||
autoupdate = 1
|
||||
template_file = "pai_secrecords.tmpl"
|
||||
ui_title = "Security Records"
|
||||
|
||||
|
||||
/datum/pai_software/sec_records/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
var/records[0]
|
||||
for(var/datum/data/record/general in sortRecord(GLOB.data_core.general))
|
||||
var/record[0]
|
||||
record["name"] = general.fields["name"]
|
||||
record["ref"] = "\ref[general]"
|
||||
records[++records.len] = record
|
||||
|
||||
data["records"] = records
|
||||
|
||||
var/datum/data/record/G = user.securityActive1
|
||||
var/datum/data/record/S = user.securityActive2
|
||||
data["general"] = G ? G.fields : null
|
||||
data["security"] = S ? S.fields : null
|
||||
data["could_not_find"] = user.security_cannotfind
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/sec_records/Topic(href, href_list)
|
||||
var/mob/living/silicon/pai/P = usr
|
||||
if(!istype(P)) return
|
||||
|
||||
if(href_list["select"])
|
||||
var/datum/data/record/record = locate(href_list["select"])
|
||||
if(record)
|
||||
var/datum/data/record/R = record
|
||||
var/datum/data/record/S = null
|
||||
if(!( GLOB.data_core.general.Find(R) ))
|
||||
P.securityActive1 = null
|
||||
P.securityActive2 = null
|
||||
P.security_cannotfind = 1
|
||||
else
|
||||
P.security_cannotfind = 0
|
||||
for(var/datum/data/record/E in GLOB.data_core.security)
|
||||
if((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"]))
|
||||
S = E
|
||||
P.securityActive1 = R
|
||||
P.securityActive2 = S
|
||||
else
|
||||
P.securityActive1 = null
|
||||
P.securityActive2 = null
|
||||
P.security_cannotfind = 1
|
||||
return 1
|
||||
|
||||
/datum/pai_software/door_jack
|
||||
name = "Door Jack"
|
||||
ram_cost = 30
|
||||
id = "door_jack"
|
||||
toggle = 0
|
||||
|
||||
autoupdate = 1
|
||||
template_file = "pai_doorjack.tmpl"
|
||||
ui_title = "Door Jack"
|
||||
ui_width = 300
|
||||
ui_height = 150
|
||||
|
||||
/datum/pai_software/door_jack/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
data["cable"] = user.cable != null
|
||||
data["machine"] = user.cable && (user.cable.machine != null)
|
||||
data["inprogress"] = user.hackdoor != null
|
||||
data["progress_a"] = round(user.hackprogress / 10)
|
||||
data["progress_b"] = user.hackprogress % 10
|
||||
data["aborted"] = user.hack_aborted
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/door_jack/Topic(href, href_list)
|
||||
var/mob/living/silicon/pai/P = usr
|
||||
if(!istype(P)) return
|
||||
|
||||
if(href_list["jack"])
|
||||
if(P.cable && P.cable.machine)
|
||||
P.hackdoor = P.cable.machine
|
||||
P.hackloop()
|
||||
return 1
|
||||
else if(href_list["cancel"])
|
||||
P.hackdoor = null
|
||||
return 1
|
||||
else if(href_list["cable"])
|
||||
var/turf/T = get_turf_or_move(P.loc)
|
||||
P.hack_aborted = 0
|
||||
P.cable = new /obj/item/pai_cable(T)
|
||||
for(var/mob/M in viewers(T))
|
||||
M.show_message("<span class='warning'>A port on [P] opens to reveal [P.cable], which promptly falls to the floor.</span>", 3,
|
||||
"<span class='warning'>You hear the soft click of something light and hard falling to the ground.</span>", 2)
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/pai/proc/hackloop()
|
||||
var/obj/machinery/door/D = cable.machine
|
||||
if(!istype(D))
|
||||
hack_aborted = 1
|
||||
hackprogress = 0
|
||||
cable.machine = null
|
||||
hackdoor = null
|
||||
return
|
||||
while(hackprogress < 1000)
|
||||
if(cable && cable.machine == D && cable.machine == hackdoor && get_dist(src, hackdoor) <= 1)
|
||||
hackprogress = min(hackprogress+rand(1, 20), 1000)
|
||||
else
|
||||
hack_aborted = 1
|
||||
hackprogress = 0
|
||||
hackdoor = null
|
||||
return
|
||||
if(hackprogress >= 1000)
|
||||
hackprogress = 0
|
||||
D.open()
|
||||
cable.machine = null
|
||||
return
|
||||
sleep(10) // Update every second
|
||||
|
||||
/datum/pai_software/atmosphere_sensor
|
||||
name = "Atmosphere Sensor"
|
||||
ram_cost = 5
|
||||
id = "atmos_sense"
|
||||
toggle = 0
|
||||
|
||||
template_file = "pai_atmosphere.tmpl"
|
||||
ui_title = "Atmosphere Sensor"
|
||||
ui_width = 350
|
||||
ui_height = 300
|
||||
|
||||
|
||||
/datum/pai_software/atmosphere_sensor/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
var/turf/T = get_turf_or_move(user.loc)
|
||||
if(!T)
|
||||
data["reading"] = 0
|
||||
data["pressure"] = 0
|
||||
data["temperature"] = 0
|
||||
data["temperatureC"] = 0
|
||||
data["gas"] = list()
|
||||
else
|
||||
var/datum/gas_mixture/env = T.return_air()
|
||||
data["reading"] = 1
|
||||
data["pressure"] = env.return_pressure()
|
||||
data["temperature"] = round(env.temperature)
|
||||
data["temperatureC"] = round(env.temperature-T0C)
|
||||
|
||||
var/t_moles = env.total_moles()
|
||||
var/gases[0]
|
||||
if(t_moles)
|
||||
var/n2[0]
|
||||
n2["name"] = "Nitrogen"
|
||||
n2["percent"] = round((env.nitrogen/t_moles)*100)
|
||||
var/o2[0]
|
||||
o2["name"] = "Oxygen"
|
||||
o2["percent"] = round((env.oxygen/t_moles)*100)
|
||||
var/co2[0]
|
||||
co2["name"] = "Carbon Dioxide"
|
||||
co2["percent"] = round((env.carbon_dioxide/t_moles)*100)
|
||||
var/plasma[0]
|
||||
plasma["name"] = "Plasma"
|
||||
plasma["percent"] = round((env.toxins/t_moles)*100)
|
||||
var/other[0]
|
||||
other["name"] = "Other"
|
||||
other["percent"] = round(1-((env.oxygen/t_moles)+(env.nitrogen/t_moles)+(env.carbon_dioxide/t_moles)+(env.toxins/t_moles)))
|
||||
gases[++gases.len] = n2
|
||||
gases[++gases.len] = o2
|
||||
gases[++gases.len] = co2
|
||||
gases[++gases.len] = plasma
|
||||
gases[++gases.len] = other
|
||||
data["gas"] = gases
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/sec_hud
|
||||
name = "Security HUD"
|
||||
ram_cost = 20
|
||||
id = "sec_hud"
|
||||
|
||||
/datum/pai_software/sec_hud/toggle(mob/living/silicon/pai/user)
|
||||
user.secHUD = !user.secHUD
|
||||
user.remove_med_sec_hud()
|
||||
if(user.secHUD)
|
||||
user.add_sec_hud()
|
||||
|
||||
/datum/pai_software/sec_hud/is_active(mob/living/silicon/pai/user)
|
||||
return user.secHUD
|
||||
|
||||
/datum/pai_software/med_hud
|
||||
name = "Medical HUD"
|
||||
ram_cost = 20
|
||||
id = "med_hud"
|
||||
|
||||
/datum/pai_software/med_hud/toggle(mob/living/silicon/pai/user)
|
||||
user.medHUD = !user.medHUD
|
||||
user.remove_med_sec_hud()
|
||||
if(user.medHUD)
|
||||
user.add_med_hud()
|
||||
|
||||
/datum/pai_software/med_hud/is_active(mob/living/silicon/pai/user)
|
||||
return user.medHUD
|
||||
|
||||
/datum/pai_software/translator
|
||||
name = "Universal Translator"
|
||||
ram_cost = 35
|
||||
id = "translator"
|
||||
|
||||
/datum/pai_software/translator/toggle(mob/living/silicon/pai/user)
|
||||
// Galactic Common, Sol Common, Tradeband, Gutter and Trinary are added with New() and are therefore the current default, always active languages
|
||||
user.translator_on = !user.translator_on
|
||||
if(user.translator_on)
|
||||
user.add_language("Sinta'unathi")
|
||||
user.add_language("Siik'tajr")
|
||||
user.add_language("Canilunzt")
|
||||
user.add_language("Skrellian")
|
||||
user.add_language("Vox-pidgin")
|
||||
user.add_language("Rootspeak")
|
||||
user.add_language("Chittin")
|
||||
user.add_language("Bubblish")
|
||||
user.add_language("Orluum")
|
||||
user.add_language("Clownish")
|
||||
user.add_language("Neo-Russkiya")
|
||||
else
|
||||
user.remove_language("Sinta'unathi")
|
||||
user.remove_language("Siik'tajr")
|
||||
user.remove_language("Canilunzt")
|
||||
user.remove_language("Skrellian")
|
||||
user.remove_language("Vox-pidgin")
|
||||
user.remove_language("Rootspeak")
|
||||
user.remove_language("Chittin")
|
||||
user.remove_language("Bubblish")
|
||||
user.remove_language("Orluum")
|
||||
user.remove_language("Clownish")
|
||||
user.remove_language("Neo-Russkiya")
|
||||
|
||||
/datum/pai_software/translator/is_active(mob/living/silicon/pai/user)
|
||||
return user.translator_on
|
||||
|
||||
/datum/pai_software/signaller
|
||||
name = "Remote Signaller"
|
||||
ram_cost = 5
|
||||
id = "signaller"
|
||||
toggle = 0
|
||||
|
||||
template_file = "pai_signaller.tmpl"
|
||||
ui_title = "Signaller"
|
||||
ui_width = 320
|
||||
ui_height = 150
|
||||
|
||||
/datum/pai_software/signaller/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
data["frequency"] = format_frequency(user.sradio.frequency)
|
||||
data["code"] = user.sradio.code
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/signaller/Topic(href, href_list)
|
||||
var/mob/living/silicon/pai/P = usr
|
||||
if(!istype(P)) return
|
||||
|
||||
if(href_list["send"])
|
||||
P.sradio.send_signal("ACTIVATE")
|
||||
for(var/mob/O in hearers(1, P.loc))
|
||||
O.show_message("[bicon(P)] *beep* *beep*", 3, "*beep* *beep*", 2)
|
||||
return 1
|
||||
|
||||
else if(href_list["freq"])
|
||||
var/new_frequency = (P.sradio.frequency + text2num(href_list["freq"]))
|
||||
if(new_frequency < PUBLIC_LOW_FREQ || new_frequency > PUBLIC_HIGH_FREQ)
|
||||
new_frequency = sanitize_frequency(new_frequency)
|
||||
P.sradio.set_frequency(new_frequency)
|
||||
return 1
|
||||
|
||||
else if(href_list["code"])
|
||||
P.sradio.code += text2num(href_list["code"])
|
||||
P.sradio.code = round(P.sradio.code)
|
||||
P.sradio.code = min(100, P.sradio.code)
|
||||
P.sradio.code = max(1, P.sradio.code)
|
||||
return 1
|
||||
|
||||
/datum/pai_software/host_scan
|
||||
name = "Host Bioscan"
|
||||
ram_cost = 5
|
||||
id = "bioscan"
|
||||
toggle = 0
|
||||
|
||||
template_file = "pai_bioscan.tmpl"
|
||||
ui_title = "Host Bioscan"
|
||||
ui_width = 400
|
||||
ui_height = 350
|
||||
|
||||
/datum/pai_software/host_scan/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
var/mob/living/held = user.loc
|
||||
var/count = 0
|
||||
|
||||
// Find the carrier
|
||||
while(!isliving(held))
|
||||
if(!held || !held.loc || count > 6)
|
||||
//For a runtime where M ends up in nullspace (similar to bluespace but less colourful)
|
||||
to_chat(user, "You are not being carried by anyone!")
|
||||
return 0
|
||||
held = held.loc
|
||||
count++
|
||||
if(isliving(held))
|
||||
data["holder"] = held
|
||||
data["health"] = "[held.stat > 1 ? "dead" : "[held.health]% healthy"]"
|
||||
data["brute"] = "[held.getBruteLoss() > 50 ? "<font color=#FF5555>" : "<font color=#55FF55>"][held.getBruteLoss()]</font>"
|
||||
data["oxy"] = "[held.getOxyLoss() > 50 ? "<font color=#FF5555>" : "<font color=#55FF55>"][held.getOxyLoss()]</font>"
|
||||
data["tox"] = "[held.getToxLoss() > 50 ? "<font color=#FF5555>" : "<font color=#55FF55>"][held.getToxLoss()]</font>"
|
||||
data["burn"] = "[held.getFireLoss() > 50 ? "<font color=#FF5555>" : "<font color=#55FF55>"][held.getFireLoss()]</font>"
|
||||
data["temp"] = "[held.bodytemperature-T0C]°C ([held.bodytemperature*1.8-459.67]°F)"
|
||||
else
|
||||
data["holder"] = 0
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/flashlight
|
||||
name = "Flashlight"
|
||||
ram_cost = 5
|
||||
id = "flashlight"
|
||||
|
||||
/datum/pai_software/flashlight/toggle(mob/living/silicon/pai/user)
|
||||
var/atom/movable/actual_location = istype(user.loc, /obj/item/paicard) ? user.loc : user
|
||||
if(!user.flashlight_on)
|
||||
actual_location.set_light(2)
|
||||
user.card.set_light(2)
|
||||
else
|
||||
actual_location.set_light(0)
|
||||
user.card.set_light(0)
|
||||
|
||||
user.flashlight_on = !user.flashlight_on
|
||||
|
||||
/datum/pai_software/flashlight/is_active(mob/living/silicon/pai/user)
|
||||
return user.flashlight_on
|
||||
@@ -97,8 +97,8 @@
|
||||
/obj/item/pda/silicon/pai/can_use()
|
||||
var/mob/living/silicon/pai/pAI = usr
|
||||
if(!istype(pAI))
|
||||
return 0
|
||||
if(!pAI.software["messenger"])
|
||||
return FALSE
|
||||
if(!pAI.installed_software["messenger"])
|
||||
to_chat(usr, "<span class='warning'>You have not purchased the digital messenger!</span>")
|
||||
return 0
|
||||
return FALSE
|
||||
return ..() && !pAI.silence_time
|
||||
|
||||
Reference in New Issue
Block a user