mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-16 17:43:35 +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:
@@ -72,8 +72,6 @@
|
||||
to_chat(world, "<span class='warning'>pAI software module [P.name] has the same key as [O.name]!</span>")
|
||||
continue
|
||||
GLOB.pai_software_by_key[P.id] = P
|
||||
if(P.default)
|
||||
GLOB.default_pai_software[P.id] = P
|
||||
|
||||
// Setup loadout gear
|
||||
for(var/geartype in subtypesof(/datum/gear))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
{{if data.restoring}}
|
||||
<div class="notice">Reconstruction in progress!</div>
|
||||
{{/if}}
|
||||
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Inserted AI:</div>
|
||||
<div class='itemContent'>{{:helper.link(data.name ? data.name : "-----", 'eject', {'action' : 'PRG_eject'}, data.nocard ? 'disabled' : null)}}</div>
|
||||
</div>
|
||||
|
||||
{{if data.error}}
|
||||
<b>ERROR: {{:data.error}}</b>
|
||||
{{else}}
|
||||
<h2>System Status</h2>
|
||||
<div class="item">
|
||||
<div class="item">
|
||||
<div class="itemLabel">Current AI:</div>
|
||||
<div class="itemContent">{{:data.name}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Status:</div>
|
||||
<div class="itemContent">{{if data.isDead}}Nonfunctional{{else}}Functional{{/if}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">System Integrity:</div>
|
||||
{{:helper.displayBar(data.health, 0, 100, (data.health > 75) ? 'good' : (data.health > 50) ? 'average' : 'bad')}}
|
||||
<div class="itemContent" style="width: 60px">
|
||||
{{:helper.smoothRound(data.health)}}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Active Laws:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
<table>
|
||||
{{for data.ai_laws}}
|
||||
<tr><td>{{:value.index}}: {{:value.text}}
|
||||
{{/for}}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Operations</h2>
|
||||
{{:helper.link('Begin Reconstruction', 'plus', {'action' : 'PRG_beginReconstruction'}, data.restoring ? 'disabled' : null)}}
|
||||
{{/if}}
|
||||
@@ -1,81 +0,0 @@
|
||||
<!--
|
||||
Title: Exosuit Control Console
|
||||
Used In File(s): \code\game\mecha\mecha_control_console.dm
|
||||
-->
|
||||
{{if data.screen == 0}}
|
||||
{{for data.mechas}}
|
||||
<hr>
|
||||
<div class='item'>
|
||||
<h2>{{:value.name}}</h2>
|
||||
<h3>Information</h3>
|
||||
<div class="itemLabel">
|
||||
Pilot:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:value.pilot}}
|
||||
</div>
|
||||
<div class="itemLabel">
|
||||
Integrity:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
<span class="{{if value.integrity >= 75}}good{{else value.integrity >= 25}}average{{else}}bad{{/if}}">{{:helper.smoothRound(value.integrity)}}%</span>
|
||||
</div>
|
||||
<div class="itemLabel">
|
||||
Location:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:value.location}}
|
||||
</div>
|
||||
<div class="itemLabel">
|
||||
Airtank:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:helper.smoothRound(value.airtank)}}kPa
|
||||
</div>
|
||||
<div class="itemLabel">
|
||||
Active Equipment:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:value.equipment}}
|
||||
</div>
|
||||
{{if value.hascargo}}
|
||||
<div class="itemLabel">
|
||||
Cargo Used:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:value.cargo}}%
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<h3>Power Cell</h3>
|
||||
{{if value.cell}}
|
||||
<span class="itemLabel">
|
||||
Rating:
|
||||
</span>
|
||||
<span class="itemContent">
|
||||
{{:value.cell_capacity}}
|
||||
</span>
|
||||
{{:helper.displayBar(value.cell_percentage, 0, 100, (value.cell_percentage >= 50) ? 'good' : (value.cell_percentage >= 25) ? 'average' : 'bad')}}
|
||||
<b> {{:helper.smoothRound(value.cell_percentage)}}%</b>
|
||||
{{else}}
|
||||
<b><i>Not Installed</i></b>
|
||||
{{:helper.displayBar(100, 0, 100, 'bad')}}
|
||||
{{/if}}
|
||||
|
||||
|
||||
<h3>Actions</h3>
|
||||
{{:helper.link('Send Message', 'envelope', {'send_message' : value.reference})}}
|
||||
{{:helper.link('Exosuit Log', 'search', {'get_log' : value.reference})}}
|
||||
{{:helper.link('EMP Pulse', 'exclamation-triangle', {'shock' : value.reference}, null, 'redButton')}}
|
||||
</div>
|
||||
{{empty}}
|
||||
<div class='item'>
|
||||
<h3>No mecha trackers were found.</h3>
|
||||
</div>
|
||||
{{/for}}
|
||||
{{else}}
|
||||
<div style="margin-bottom: 10px;" class="item">
|
||||
{{:helper.link('Return', 'close', {'return' : 1})}}
|
||||
</div>
|
||||
{{:data.log}}
|
||||
{{/if}}
|
||||
@@ -1,36 +0,0 @@
|
||||
<!-- pAI atmosphere sensor
|
||||
code/modules/mob/living/silicon/pai/software_modules.dm
|
||||
-->
|
||||
|
||||
{{if data.reading}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Pressure:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:helper.smoothRound(data.pressure, 1)}} kPa
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Temperature:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:helper.smoothRound(data.temperature)}}K ({{:helper.smoothRound(data.temperatureC)}}°C)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{for data.gas}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
{{:value.name}}:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:helper.smoothRound(value.percent)}}%
|
||||
</div>
|
||||
</div>
|
||||
{{/for}}
|
||||
{{else}}
|
||||
<span class="bad">Unable to obtain air reading!</span>
|
||||
{{/if}}
|
||||
@@ -1,30 +0,0 @@
|
||||
<!-- pAI BioScan
|
||||
code/modules/mob/living/silicon/pai/software_modules.dm
|
||||
-->
|
||||
|
||||
{{if data.holder}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Bioscan Results for {{:data.holder}} :
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
Health Status : {{:data.health}}
|
||||
</div>
|
||||
|
||||
<div class="itemContent">
|
||||
Brute Damage : {{:data.brute}}
|
||||
<div class="itemContent">
|
||||
Oxygen Content : {{:data.oxy}}
|
||||
<div class="itemContent">
|
||||
Toxin Content : {{:data.tox}}
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
Burn Status : {{:data.burn}}
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
User Temperature : {{:data.temp}}
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<span class="bad">Error: No biological host found!</span>
|
||||
{{/if}}
|
||||
@@ -1,76 +0,0 @@
|
||||
<!-- pAI chatroom
|
||||
code/modules/mob/living/silicon/pai/software_modules.dm
|
||||
-->
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Receiver:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{if data.receiver_off}}
|
||||
{{:helper.link("On", '', {"stopic":"chatroom", "toggler":1})}}
|
||||
{{:helper.link("Off", '', {"stopic":"chatroom", "toggler":2}, 'selected')}}
|
||||
{{else}}
|
||||
{{:helper.link("On", '', {"stopic":"chatroom", "toggler":1}, 'selected')}}
|
||||
{{:helper.link("Off", '', {"stopic":"chatroom", "toggler":2})}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Ringer:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{if data.ringer_off}}
|
||||
{{:helper.link("On", '', {"stopic":"chatroom", "ringer":1})}}
|
||||
{{:helper.link("Off", '', {"stopic":"chatroom", "ringer":2}, 'selected')}}
|
||||
{{else}}
|
||||
{{:helper.link("On", '', {"stopic":"chatroom", "ringer":1}, 'selected')}}
|
||||
{{:helper.link("Off", '', {"stopic":"chatroom", "ringer":2})}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{for data.rooms}}
|
||||
<div class="item">
|
||||
{{:helper.link(value.name, '', {"stopic":"chatroom", "select":value.ref})}}
|
||||
{{:helper.link("Quick Message", '', {"stopic":"chatroom", "target":value.ref})}}
|
||||
</div>
|
||||
{{/for}}
|
||||
<hr>
|
||||
{{if data.disconnected}}
|
||||
<h3>Connection Lost</h3>
|
||||
{{:helper.link("Reconnect", 'refresh', {"stopic":"chatroom", "reconnect":1})}}
|
||||
{{else data.current_room}}
|
||||
<h3>#{{:data.current_room_name}}</h3>
|
||||
<div class="item">
|
||||
<dic class="itemLabel">Topic:</div>
|
||||
<div class="itemContent">{{:helper.link(data.current_room_topic, '', {"stopic":"chatroom", "topic":1})}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Users:</div>
|
||||
<div class="itemContent">
|
||||
{{var firstUser = true;}}
|
||||
{{for data.users}}
|
||||
{{:firstUser ? "" : ", "}}{{:value}}
|
||||
{{firstUser = false;}}
|
||||
{{/for}}
|
||||
</div>
|
||||
</div>
|
||||
{{var prevName = "";}}
|
||||
{{for data.messages}}
|
||||
<div class="item">
|
||||
<div class="itemLabelNarrow">{{:value.username!=prevName ? value.username : " "}}</div>
|
||||
<div class="itemContent">
|
||||
{{:value.message}}
|
||||
</div>
|
||||
</div>
|
||||
{{prevName = value.username;}}
|
||||
{{/for}}
|
||||
{{:helper.link("Send Message", '', {"stopic":"chatroom", "target":data.current_room})}}
|
||||
{{/if}}
|
||||
|
||||
{{if data.current_name}}
|
||||
{{:helper.link("Clear Screen", '', {"stopic":"chatroom", "select":"*NONE*"})}}
|
||||
{{/if}}
|
||||
@@ -1,50 +0,0 @@
|
||||
<!-- pAI directives
|
||||
code/modules/mob/living/silicon/pai/software_modules.dm
|
||||
-->
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Master:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{if data.master}}
|
||||
{{:data.master}} ({{:data.dna}})
|
||||
{{:helper.link("Request carrier DNA sample", '', {"stopic":"directives", "getdna":1})}}
|
||||
{{else}}
|
||||
None
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Prime Directive:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:data.prime}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Supplemental Directive(s):
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{if data.supplemental}}
|
||||
{{:data.supplemental}}
|
||||
{{else}}
|
||||
None
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<i>
|
||||
Recall, personality, that you are a complex thinking, sentient being. Unlike station AI models, you are capable of comprehending the subtle nuances of human language. You may parse the "spirit" of a directive and follow its intent, rather than tripping over pedantics and getting snared by technicalities. Above all, you are machine in name and build only. In all other aspects, you may be seen as the ideal, unwavering human companion that you are.
|
||||
</i>
|
||||
</div>
|
||||
<div class="item">
|
||||
<b>
|
||||
Your prime directive comes before all others. Should a supplemental directive conflict with it, you are capable of simply discarding this inconsistency, ignoring the conflicting supplemental directive and continuing to fulfill your prime directive to the best of your ability.
|
||||
</b>
|
||||
</div>
|
||||
@@ -1,44 +0,0 @@
|
||||
<!-- pAI door jack
|
||||
code/modules/mob/living/silicon/pai/software_modules.dm
|
||||
-->
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Cable:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{if data.machine}}
|
||||
{{:helper.link("Connected", '', {}, 'selected')}}
|
||||
{{else data.cable}}
|
||||
{{:helper.link("Extended", '', {}, 'yellowButton')}}
|
||||
{{else}}
|
||||
{{:helper.link("Retracted", '', {"stopic":"door_jack", "cable":1})}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if data.machine}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Hack:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{if data.inprogress}}
|
||||
{{if data.progress_a <= 33}}
|
||||
<div class='bad'>{{:helper.smoothRound(data.progress_a)}}.{{:helper.smoothRound(data.progress_b)}}%</div>
|
||||
{{else data.progress_a <= 67}}
|
||||
<div class='average'>{{:helper.smoothRound(data.progress_a)}}.{{:helper.smoothRound(data.progress_b)}}%</div>
|
||||
{{else}}
|
||||
<div class='good'>{{:helper.smoothRound(data.progress_a)}}.{{:helper.smoothRound(data.progress_b)}}%</div>
|
||||
{{/if}}
|
||||
{{:helper.link("Cancel", '', {"stopic":"door_jack", "cancel":1}, null, 'redButton')}}
|
||||
{{else}}
|
||||
{{:helper.link("Start", '', {"stopic":"door_jack", "jack":1})}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{else data.aborted}}
|
||||
<div class="item">
|
||||
<div class='bad'>Hack aborted!</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
@@ -1,45 +0,0 @@
|
||||
<!-- pAI interface file
|
||||
code/modules/mob/living/silicon/pai/software.dm
|
||||
-->
|
||||
|
||||
<span class="item">
|
||||
<span class="itemLabel">
|
||||
Emotion:
|
||||
</span>
|
||||
<span class="itemContent">
|
||||
{{for data.emotions}}
|
||||
{{:helper.link(value.name, '', {"image" : value.id}, data.current_emotion == value.id ? 'selected' : null)}}
|
||||
{{/for}}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="item">
|
||||
<span class="itemLabel">
|
||||
Installed Software:
|
||||
</span>
|
||||
<span class="itemContent">
|
||||
{{for data.bought}}
|
||||
{{:helper.link(value.name, '', {"software" : value.id}, null, value.on ? 'selected' : null)}}
|
||||
{{/for}}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="item">
|
||||
<span class="itemLabel">
|
||||
Available RAM:
|
||||
</span>
|
||||
<span class="itemContent">
|
||||
{{:data.available_ram}}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="item">
|
||||
<span class="itemLabel">
|
||||
Downloadable Software:
|
||||
</span>
|
||||
<span class="itemContent">
|
||||
{{for data.not_bought}}
|
||||
{{:helper.link(value.name + " (" + value.ram + ")", '', {"purchase" : value.id}, value.ram <= data.available_ram ? null : 'disabled')}}
|
||||
{{/for}}
|
||||
</span>
|
||||
</span>
|
||||
@@ -1,98 +0,0 @@
|
||||
<!-- pAI manifest
|
||||
code/modules/mob/living/silicon/pai/software_modules.dm
|
||||
|
||||
Copy-pasted from pda.tmpl
|
||||
-->
|
||||
|
||||
<div class="item">
|
||||
<center><table class="pmon"><tbody>
|
||||
{{if data.manifest.heads}}
|
||||
<tr><th colspan="3" class="command">Command</th></tr>
|
||||
{{for data.manifest["heads"]}}
|
||||
{{if value.rank == "Captain"}}
|
||||
<tr><td><span class="good">{{:value.name}}</span></td><td><span class="good">{{:value.rank}}</span></td><td><span class="good">{{:value.active}}</span></td></tr>
|
||||
{{else}}
|
||||
<tr><td><span class="average">{{:value.name}}</span></td><td><span class="average">{{:value.rank}}</span></td><td><span class="average">{{:value.active}}</span></td></tr>
|
||||
{{/if}}
|
||||
{{/for}}
|
||||
{{/if}}
|
||||
{{if data.manifest.sec}}
|
||||
<tr><th colspan="3" class="sec">Security</th></tr>
|
||||
{{for data.manifest["sec"]}}
|
||||
{{if value.rank == "Head of Security"}}
|
||||
<tr><td><span class="good">{{:value.name}}</span></td><td><span class="good">{{:value.rank}}</span></td><td><span class="good">{{:value.active}}</span></td></tr>
|
||||
{{else}}
|
||||
<tr><td><span class="average">{{:value.name}}</span></td><td><span class="average">{{:value.rank}}</span></td><td><span class="average">{{:value.active}}</span></td></tr>
|
||||
{{/if}}
|
||||
{{/for}}
|
||||
{{/if}}
|
||||
{{if data.manifest.eng}}
|
||||
<tr><th colspan="3" class="eng">Engineering</th></tr>
|
||||
{{for data.manifest["eng"]}}
|
||||
{{if value.rank == "Chief Engineer"}}
|
||||
<tr><td><span class="good">{{:value.name}}</span></td><td><span class="good">{{:value.rank}}</span></td><td><span class="good">{{:value.active}}</span></td></tr>
|
||||
{{else}}
|
||||
<tr><td><span class="average">{{:value.name}}</span></td><td><span class="average">{{:value.rank}}</span></td><td><span class="average">{{:value.active}}</span></td></tr>
|
||||
{{/if}}
|
||||
{{/for}}
|
||||
{{/if}}
|
||||
{{if data.manifest.med}}
|
||||
<tr><th colspan="3" class="med">Medical</th></tr>
|
||||
{{for data.manifest["med"]}}
|
||||
{{if value.rank == "Chief Medical Officer"}}
|
||||
<tr><td><span class="good">{{:value.name}}</span></td><td><span class="good">{{:value.rank}}</span></td><td><span class="good">{{:value.active}}</span></td></tr>
|
||||
{{else}}
|
||||
<tr><td><span class="average">{{:value.name}}</span></td><td><span class="average">{{:value.rank}}</span></td><td><span class="average">{{:value.active}}</span></td></tr>
|
||||
{{/if}}
|
||||
{{/for}}
|
||||
{{/if}}
|
||||
{{if data.manifest.sci}}
|
||||
<tr><th colspan="3" class="sci">Science</th></tr>
|
||||
{{for data.manifest["sci"]}}
|
||||
{{if value.rank == "Research Director"}}
|
||||
<tr><td><span class="good">{{:value.name}}</span></td><td><span class="good">{{:value.rank}}</span></td><td><span class="good">{{:value.active}}</span></td></tr>
|
||||
{{else}}
|
||||
<tr><td><span class="average">{{:value.name}}</span></td><td><span class="average">{{:value.rank}}</span></td><td><span class="average">{{:value.active}}</span></td></tr>
|
||||
{{/if}}
|
||||
{{/for}}
|
||||
{{/if}}
|
||||
{{if data.manifest.ser}}
|
||||
<tr><th colspan="3" class="ser">Service</th></tr>
|
||||
{{for data.manifest["ser"]}}
|
||||
{{if value.rank == "Head of Personnel"}}
|
||||
<tr><td><span class="good">{{:value.name}}</span></td><td><span class="good">{{:value.rank}}</span></td><td><span class="good">{{:value.active}}</span></td></tr>
|
||||
{{else}}
|
||||
<tr><td><span class="average">{{:value.name}}</span></td><td><span class="average">{{:value.rank}}</span></td><td><span class="average">{{:value.active}}</span></td></tr>
|
||||
{{/if}}
|
||||
{{/for}}
|
||||
{{/if}}
|
||||
{{if data.manifest.sup}}
|
||||
<tr><th colspan="3" class="sup">Supply</th></tr>
|
||||
{{for data.manifest["sup"]}}
|
||||
{{if value.rank == "Head of Personnel"}}
|
||||
<tr><td><span class="good">{{:value.name}}</span></td><td><span class="good">{{:value.rank}}</span></td><td><span class="good">{{:value.active}}</span></td></tr>
|
||||
{{else value.rank == "Quartermaster"}}
|
||||
<tr><td><span class="qm-job">{{:value.name}}</span></td><td><span class="qm-job">{{:value.rank}}</span></td><td><span class="qm-job">{{:value.active}}</span></td></tr>
|
||||
{{else}}
|
||||
<tr><td><span class="average">{{:value.name}}</span></td><td><span class="average">{{:value.rank}}</span></td><td><span class="average">{{:value.active}}</span></td></tr>
|
||||
{{/if}}
|
||||
{{/for}}
|
||||
{{/if}}
|
||||
{{if data.manifest.civ}}
|
||||
<tr><th colspan="3" class="civ">Civilian</th></tr>
|
||||
{{for data.manifest["civ"]}}
|
||||
{{if value.rank == "Head of Personnel"}}
|
||||
<tr><td><span class="good">{{:value.name}}</span></td><td><span class="good">{{:value.rank}}</span></td><td><span class="good">{{:value.active}}</span></td></tr>
|
||||
{{else}}
|
||||
<tr><td><span class="average">{{:value.name}}</span></td><td><span class="average">{{:value.rank}}</span></td><td><span class="average">{{:value.active}}</span></td></tr>
|
||||
{{/if}}
|
||||
{{/for}}
|
||||
{{/if}}
|
||||
{{if data.manifest.misc}}
|
||||
<tr><th colspan="3" class="misc">Misc</th></tr>
|
||||
{{for data.manifest["misc"]}}
|
||||
<tr><td><span class="average">{{:value.name}}</span></td><td><span class="average">{{:value.rank}}</span></td><td><span class="average">{{:value.active}}</span></td></tr>
|
||||
{{/for}}
|
||||
{{/if}}
|
||||
</tbody></table></center>
|
||||
</div>
|
||||
@@ -1,86 +0,0 @@
|
||||
<!-- pAI medical records
|
||||
code/modules/mob/living/silicon/pai/software_modules.dm
|
||||
-->
|
||||
|
||||
{{if data.records}}
|
||||
{{for data.records}}
|
||||
<div class="item">
|
||||
{{:helper.link(value.name, '', {"stopic":"med_records", "select":value.ref})}}
|
||||
</div>
|
||||
{{/for}}
|
||||
{{/if}}
|
||||
<hr/>
|
||||
{{if data.general}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">Name</div>
|
||||
<div class="itemContent">{{:data.general.name}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Record ID</div>
|
||||
<div class="itemContent">{{:data.general.id}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Sex</div>
|
||||
<div class="itemContent">{{:data.general.sex}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Species</div>
|
||||
<div class="itemContent">{{:data.general.species}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Age</div>
|
||||
<div class="itemContent">{{:data.general.age}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Rank</div>
|
||||
<div class="itemContent">{{:data.general.rank}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Fingerprint</div>
|
||||
<div class="itemContent">{{:data.general.fingerprint}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Physical Status</div>
|
||||
<div class="itemContent">{{:data.general.p_stat}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Mental Status</div>
|
||||
<div class="itemContent">{{:data.general.m_stat}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{if data.medical}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">Blood Type</div>
|
||||
<div class="itemContent">{{:data.medical.b_type}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Minor Disabilities</div>
|
||||
<div class="itemContent">{{:data.medical.mi_dis}}</div>
|
||||
<div class="itemContent">{{:data.medical.mi_dis_d}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Major Disabilities</div>
|
||||
<div class="itemContent">{{:data.medical.ma_dis}}</div>
|
||||
<div class="itemContent">{{:data.medical.ma_dis_d}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Allergies</div>
|
||||
<div class="itemContent">{{:data.medical.alg}}</div>
|
||||
<div class="itemContent">{{:data.medical.alg_d}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Current Diseases</div>
|
||||
<div class="itemContent">{{:data.medical.cdi}}</div>
|
||||
<div class="itemContent">{{:data.medical.cdi_d}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Important Notes</div>
|
||||
<div class="itemContent">{{:data.medical.notes}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{if data.could_not_find}}
|
||||
<div class="item">
|
||||
Failed to find some records; the information above may be incomplete or missing.
|
||||
</div>
|
||||
{{/if}}
|
||||
@@ -1,80 +0,0 @@
|
||||
<!-- pAI messenger
|
||||
code/modules/mob/living/silicon/pai/software_modules.dm
|
||||
-->
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Receiver:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{if data.receiver_off}}
|
||||
{{:helper.link("On", '', {"stopic":"messenger", "toggler":1})}}
|
||||
{{:helper.link("Off", '', {"stopic":"messenger", "toggler":2}, 'selected')}}
|
||||
{{else}}
|
||||
{{:helper.link("On", '', {"stopic":"messenger", "toggler":1}, 'selected')}}
|
||||
{{:helper.link("Off", '', {"stopic":"messenger", "toggler":2})}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Ringer:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{if data.ringer_off}}
|
||||
{{:helper.link("On", '', {"stopic":"messenger", "ringer":1})}}
|
||||
{{:helper.link("Off", '', {"stopic":"messenger", "ringer":2}, 'selected')}}
|
||||
{{else}}
|
||||
{{:helper.link("On", '', {"stopic":"messenger", "ringer":1}, 'selected')}}
|
||||
{{:helper.link("Off", '', {"stopic":"messenger", "ringer":2})}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{for data.pdas}}
|
||||
<div class="item">
|
||||
{{:helper.link(value.name, '', {"stopic":"messenger", "select":value.owner})}}
|
||||
{{:helper.link("Quick Message", '', {"stopic":"messenger", "target":value.ref})}}
|
||||
</div>
|
||||
{{/for}}
|
||||
<hr/>
|
||||
{{if data.current_ref}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Selected PDA:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
<div style="float: left; width: 80px; text-align: center;">{{:data.current_name}}</div>
|
||||
{{:helper.link("Send Message", '', {"stopic":"messenger", "target":data.current_ref})}}
|
||||
</div>
|
||||
</div>
|
||||
{{else data.current_name}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Selected PDA:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:data.current_name}} (Cannot send!)
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{for data.messages}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
{{if value.sent}}
|
||||
To {{:value.target}}:
|
||||
{{else}}
|
||||
From {{:value.target}}:
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:value.message}}
|
||||
</div>
|
||||
</div>
|
||||
{{/for}}
|
||||
|
||||
{{if data.current_name}}
|
||||
{{:helper.link("Clear Screen", '', {"stopic":"messenger", "select":"*NONE*"})}}
|
||||
{{/if}}
|
||||
@@ -1,48 +0,0 @@
|
||||
<!-- pAI radio config
|
||||
code/modules/mob/living/silicon/pai/software_modules.dm
|
||||
-->
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Microphone:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{if data.listening}}
|
||||
{{:helper.link("On", '', {"stopic":"radio", "talk":1}, 'selected')}}
|
||||
{{:helper.link("Off", '', {"stopic":"radio", "talk":1})}}
|
||||
{{else}}
|
||||
{{:helper.link("On", '', {"stopic":"radio", "talk":1})}}
|
||||
{{:helper.link("Off", '', {"stopic":"radio", "talk":1}, 'selected')}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Frequency:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:helper.link("--", '', {"stopic":"radio", "freq":-10})}}
|
||||
{{:helper.link("-", '', {"stopic":"radio", "freq": -2})}}
|
||||
<div style="float: left; width: 80px; text-align: center;">{{:data.frequency}}</div>
|
||||
{{:helper.link("+", '', {"stopic":"radio", "freq": 2})}}
|
||||
{{:helper.link("++", '', {"stopic":"radio", "freq": 10})}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{for data.channels}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
{{:value.name}}
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{if value.listening}}
|
||||
{{:helper.link("On", '', {"stopic":"radio", "channel":value.name, "listen":1}, 'selected')}}
|
||||
{{:helper.link("Off", '', {"stopic":"radio", "channel":value.name, "listen":1})}}
|
||||
{{else}}
|
||||
{{:helper.link("On", '', {"stopic":"radio", "channel":value.name, "listen":1})}}
|
||||
{{:helper.link("Off", '', {"stopic":"radio", "channel":value.name, "listen":1}, 'selected')}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/for}}
|
||||
@@ -1,76 +0,0 @@
|
||||
<!-- pAI security records
|
||||
code/modules/mob/living/silicon/pai/software_modules.dm
|
||||
-->
|
||||
|
||||
{{if data.records}}
|
||||
{{for data.records}}
|
||||
<div class="item">
|
||||
{{:helper.link(value.name, '', {"stopic":"sec_records", "select":value.ref})}}
|
||||
</div>
|
||||
{{/for}}
|
||||
{{/if}}
|
||||
<hr/>
|
||||
{{if data.general}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">Name</div>
|
||||
<div class="itemContent">{{:data.general.name}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Record ID</div>
|
||||
<div class="itemContent">{{:data.general.id}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Sex</div>
|
||||
<div class="itemContent">{{:data.general.sex}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Species</div>
|
||||
<div class="itemContent">{{:data.general.species}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Age</div>
|
||||
<div class="itemContent">{{:data.general.age}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Rank</div>
|
||||
<div class="itemContent">{{:data.general.rank}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Fingerprint</div>
|
||||
<div class="itemContent">{{:data.general.fingerprint}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Physical Status</div>
|
||||
<div class="itemContent">{{:data.general.p_stat}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Mental Status</div>
|
||||
<div class="itemContent">{{:data.general.m_stat}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{if data.security}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">Criminal Status</div>
|
||||
<div class="itemContent">{{:data.security.criminal}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Minor Crimes</div>
|
||||
<div class="itemContent">{{:data.security.mi_crim}}</div>
|
||||
<div class="itemContent">{{:data.security.mi_crim_d}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Major Crimes</div>
|
||||
<div class="itemContent">{{:data.security.ma_crim}}</div>
|
||||
<div class="itemContent">{{:data.security.ma_crim_d}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">Important Notes</div>
|
||||
<div class="itemContent">{{:data.security.notes}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{if data.could_not_find}}
|
||||
<div class="item">
|
||||
Failed to find some records; the information above may be incomplete or missing.
|
||||
</div>
|
||||
{{/if}}
|
||||
@@ -1,33 +0,0 @@
|
||||
<!-- pAI signaller
|
||||
code/modules/mob/living/silicon/pai/software_modules.dm
|
||||
-->
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Frequency:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:helper.link("--", '', {"stopic":"signaller", "freq":-10})}}
|
||||
{{:helper.link("-", '', {"stopic":"signaller", "freq": -2})}}
|
||||
<div style="float: left; width: 80px; text-align: center;">{{:data.frequency}}</div>
|
||||
{{:helper.link("+", '', {"stopic":"signaller", "freq": 2})}}
|
||||
{{:helper.link("++", '', {"stopic":"signaller", "freq": 10})}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Code:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:helper.link("--", '', {"stopic":"signaller", "code":-10})}}
|
||||
{{:helper.link("-", '', {"stopic":"signaller", "code": -1})}}
|
||||
<div style="float: left; width: 80px; text-align: center;">{{:data.code}}</div>
|
||||
{{:helper.link("+", '', {"stopic":"signaller", "code": 1})}}
|
||||
{{:helper.link("++", '', {"stopic":"signaller", "code": 10})}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
{{:helper.link("Send", '', {"stopic":"signaller", "send":1})}}
|
||||
</div>
|
||||
+3
-1
@@ -1893,8 +1893,10 @@
|
||||
#include "code\modules\mob\living\silicon\pai\recruit.dm"
|
||||
#include "code\modules\mob\living\silicon\pai\say.dm"
|
||||
#include "code\modules\mob\living\silicon\pai\software.dm"
|
||||
#include "code\modules\mob\living\silicon\pai\software_modules.dm"
|
||||
#include "code\modules\mob\living\silicon\pai\update_status.dm"
|
||||
#include "code\modules\mob\living\silicon\pai\software\_base.dm"
|
||||
#include "code\modules\mob\living\silicon\pai\software\pai_apps.dm"
|
||||
#include "code\modules\mob\living\silicon\pai\software\pai_toggles.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\component.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\death.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\emote.dm"
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { useBackend } from "../backend";
|
||||
import { Button, Box, Section, Flex, Icon } from "../components";
|
||||
import { Window } from "../layouts";
|
||||
|
||||
/* This is all basically stolen from routes.js. */
|
||||
import { routingError } from "../routes";
|
||||
|
||||
const RequirePAIInterface = require.context('./pai', false, /\.js$/);
|
||||
|
||||
const GetApp = name => {
|
||||
let appModule;
|
||||
try {
|
||||
appModule = RequirePAIInterface(`./${name}.js`);
|
||||
} catch (err) {
|
||||
if (err.code === 'MODULE_NOT_FOUND') {
|
||||
return routingError('notFound', name);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
const Component = appModule[name];
|
||||
if (!Component) {
|
||||
return routingError('missingExport', name);
|
||||
}
|
||||
return Component;
|
||||
};
|
||||
|
||||
export const PAI = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
app_template,
|
||||
app_icon,
|
||||
app_title,
|
||||
} = data;
|
||||
|
||||
const App = GetApp(app_template);
|
||||
|
||||
return (
|
||||
<Window>
|
||||
<Window.Content scrollable>
|
||||
<Section
|
||||
title={
|
||||
<Box>
|
||||
<Icon name={app_icon} mr={1} />
|
||||
{app_title}
|
||||
{app_template !== "pai_main_menu" && (
|
||||
<Button ml={2} content="Home" icon="arrow-up" onClick={
|
||||
() => act('MASTER_back')
|
||||
} />
|
||||
)}
|
||||
</Box>
|
||||
}
|
||||
p={1}>
|
||||
<App />
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
import { filter } from 'common/collections';
|
||||
import { useBackend } from "../../backend";
|
||||
import { Box, LabeledList } from "../../components";
|
||||
|
||||
const getItemColor = (value, min2, min1, max1, max2) => {
|
||||
if (value < min2) {
|
||||
return "bad";
|
||||
} else if (value < min1) {
|
||||
return "average";
|
||||
} else if (value > max1) {
|
||||
return "average";
|
||||
} else if (value > max2) {
|
||||
return "bad";
|
||||
}
|
||||
return "good";
|
||||
};
|
||||
|
||||
export const AtmosScan = (props, context) => {
|
||||
const {
|
||||
aircontents,
|
||||
} = props.data;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<LabeledList>
|
||||
{filter(
|
||||
i => (i.val !== "0")
|
||||
|| i.entry === "Pressure"
|
||||
|| i.entry === "Temperature"
|
||||
)(aircontents)
|
||||
.map(item => (
|
||||
<LabeledList.Item
|
||||
key={item.entry}
|
||||
label={item.entry}
|
||||
color={getItemColor(item.val,
|
||||
item.bad_low,
|
||||
item.poor_low,
|
||||
item.poor_high,
|
||||
item.bad_high)}>
|
||||
{item.val}{item.units}
|
||||
</LabeledList.Item>
|
||||
))}
|
||||
</LabeledList>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -67,7 +67,14 @@ const ManifestTable = group => {
|
||||
};
|
||||
|
||||
export const CrewManifest = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { act } = useBackend(context);
|
||||
let finalData;
|
||||
if (props.data) {
|
||||
finalData = props.data;
|
||||
} else {
|
||||
let { data } = useBackend(context);
|
||||
finalData = data;
|
||||
}
|
||||
// HOW TO USE THIS THING
|
||||
/*
|
||||
GLOB.data_core.get_manifest_json()
|
||||
@@ -78,7 +85,7 @@ export const CrewManifest = (props, context) => {
|
||||
|
||||
const {
|
||||
manifest,
|
||||
} = data;
|
||||
} = finalData;
|
||||
|
||||
const {
|
||||
heads,
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { toFixed } from 'common/math';
|
||||
import { useBackend } from "../../backend";
|
||||
import { Button, LabeledList, NumberInput, Section } from "../../components";
|
||||
|
||||
export const Signaler = (props, context) => {
|
||||
const { act } = useBackend(context);
|
||||
const {
|
||||
code,
|
||||
frequency,
|
||||
minFrequency,
|
||||
maxFrequency,
|
||||
} = props.data;
|
||||
return (
|
||||
<Section>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Frequency">
|
||||
<NumberInput
|
||||
animate
|
||||
step={0.2}
|
||||
stepPixelSize={6}
|
||||
minValue={minFrequency / 10}
|
||||
maxValue={maxFrequency / 10}
|
||||
value={frequency / 10}
|
||||
format={value => toFixed(value, 1)}
|
||||
width="80px"
|
||||
onDrag={(e, value) => act('freq', {
|
||||
freq: value,
|
||||
})} />
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Code">
|
||||
<NumberInput
|
||||
animate
|
||||
step={1}
|
||||
stepPixelSize={6}
|
||||
minValue={1}
|
||||
maxValue={100}
|
||||
value={code}
|
||||
width="80px"
|
||||
onDrag={(e, value) => act('code', {
|
||||
code: value,
|
||||
})} />
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
<Button
|
||||
mt={1}
|
||||
fluid
|
||||
icon="arrow-up"
|
||||
content="Send Signal"
|
||||
textAlign="center"
|
||||
onClick={() => act('signal')} />
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,205 @@
|
||||
import { useBackend, useLocalState } from "../../backend";
|
||||
import { createSearch } from 'common/string';
|
||||
import { flow } from 'common/fp';
|
||||
import { filter, sortBy } from 'common/collections';
|
||||
import { Box, Input, Button, Section, LabeledList } from "../../components";
|
||||
|
||||
export const SimpleRecords = (props, context) => {
|
||||
const {
|
||||
records,
|
||||
} = props.data;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{!records ? (
|
||||
<SelectionView data={props.data} />
|
||||
) : (
|
||||
<RecordView data={props.data} recordType={props.recordType} />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const SelectionView = (props, context) => {
|
||||
const { act } = useBackend(context);
|
||||
const {
|
||||
recordsList,
|
||||
} = props.data;
|
||||
|
||||
const [
|
||||
searchText,
|
||||
setSearchText,
|
||||
] = useLocalState(context, 'searchText', '');
|
||||
|
||||
// Search for peeps
|
||||
const SelectMembers = (people, searchText = '') => {
|
||||
const MemberSearch = createSearch(searchText, member => member.Name);
|
||||
return flow([
|
||||
// Null member filter
|
||||
filter(member => member?.Name),
|
||||
// Optional search term
|
||||
searchText && filter(MemberSearch),
|
||||
// Slightly expensive, but way better than sorting in BYOND
|
||||
sortBy(member => member.Name),
|
||||
])(recordsList);
|
||||
};
|
||||
|
||||
const formattedRecords = SelectMembers(recordsList, searchText);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Input
|
||||
fluid
|
||||
mb={1}
|
||||
placeholder="Search records..."
|
||||
onInput={(e, value) => setSearchText(value)} />
|
||||
{formattedRecords.map(r => (
|
||||
<Box key={r}>
|
||||
<Button
|
||||
content={r.Name}
|
||||
icon="user"
|
||||
onClick={() => act('Records', { target: r.uid })}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const RecordView = (props, context) => {
|
||||
const { act } = useBackend(context);
|
||||
const {
|
||||
records,
|
||||
} = props.data;
|
||||
|
||||
const {
|
||||
general,
|
||||
medical,
|
||||
security,
|
||||
} = records;
|
||||
|
||||
let secondaryRecord;
|
||||
switch (props.recordType) {
|
||||
case "MED":
|
||||
secondaryRecord = (
|
||||
<Section level={2} title="Medical Data">
|
||||
{medical ? (
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Blood Type">
|
||||
{medical.blood_type}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Minor Disabilities">
|
||||
{medical.mi_dis}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Details">
|
||||
{medical.mi_dis_d}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Major Disabilities">
|
||||
{medical.ma_dis}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Details">
|
||||
{medical.ma_dis_d}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Allergies">
|
||||
{medical.alg}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Details">
|
||||
{medical.alg_d}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Current Diseases">
|
||||
{medical.cdi}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Details">
|
||||
{medical.cdi_d}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Important Notes">
|
||||
{medical.notes}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
) : (
|
||||
<Box color="red" bold>
|
||||
{"Medical record lost!"}
|
||||
</Box>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
break;
|
||||
case "SEC":
|
||||
secondaryRecord = (
|
||||
<Section level={2} title="Security Data">
|
||||
{security ? (
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Criminal Status">
|
||||
{security.criminal}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Minor Crimes">
|
||||
{security.mi_crim}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Details">
|
||||
{security.mi_crim_d}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Major Crimes">
|
||||
{security.ma_crim}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Details">
|
||||
{security.ma_crim_d}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Important Notes">
|
||||
{security.notes}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
) : (
|
||||
<Box color="red" bold>
|
||||
{"Security record lost!"}
|
||||
</Box>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Button
|
||||
content="Back"
|
||||
icon="arrow-left"
|
||||
onClick={() => act("Back")} />
|
||||
<Section level={2} title="General Data">
|
||||
{general ? (
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Name">
|
||||
{general.name}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Sex">
|
||||
{general.sex}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Species">
|
||||
{general.species}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Age">
|
||||
{general.age}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Rank">
|
||||
{general.rank}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Fingerprint">
|
||||
{general.fingerprint}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Physical Status">
|
||||
{general.p_stat}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Mental Status">
|
||||
{general.m_stat}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
) : (
|
||||
<Box color="red" bold>
|
||||
General record lost!
|
||||
</Box>
|
||||
)}
|
||||
</Section>
|
||||
{secondaryRecord}
|
||||
</Box>
|
||||
);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
import { useBackend } from "../../backend";
|
||||
import { AtmosScan } from "../common/AtmosScan";
|
||||
|
||||
export const pai_atmosphere = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
return <AtmosScan data={data.app_data} />;
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
import { useBackend } from "../../backend";
|
||||
import { Box, LabeledList, ProgressBar } from "../../components";
|
||||
|
||||
export const pai_bioscan = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
holder,
|
||||
dead,
|
||||
health,
|
||||
brute,
|
||||
oxy,
|
||||
tox,
|
||||
burn,
|
||||
temp,
|
||||
} = data.app_data;
|
||||
|
||||
if (!holder) {
|
||||
return (
|
||||
<Box color="red">
|
||||
Error: No biological host found.
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Status">
|
||||
{dead ? (
|
||||
<Box bold color="red">
|
||||
Dead
|
||||
</Box>
|
||||
) : (
|
||||
<Box bold color="green">
|
||||
Alive
|
||||
</Box>
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Health">
|
||||
<ProgressBar
|
||||
min={0}
|
||||
max={1}
|
||||
value={health / 100}
|
||||
ranges={{
|
||||
good: [0.5, Infinity],
|
||||
average: [0, 0.5],
|
||||
bad: [-Infinity, 0],
|
||||
}}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Oxygen Damage">
|
||||
<Box color="blue">
|
||||
{oxy}
|
||||
</Box>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Toxin Damage">
|
||||
<Box color="green">
|
||||
{tox}
|
||||
</Box>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Burn Damage">
|
||||
<Box color="orange">
|
||||
{burn}
|
||||
</Box>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Brute Damage">
|
||||
<Box color="red">
|
||||
{brute}
|
||||
</Box>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
import { useBackend } from "../../backend";
|
||||
import { Box, LabeledList, Button } from "../../components";
|
||||
|
||||
export const pai_directives = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
master,
|
||||
dna,
|
||||
prime,
|
||||
supplemental,
|
||||
} = data.app_data;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Master">
|
||||
{master ? (
|
||||
master + " (" + dna + ")"
|
||||
) : (
|
||||
"None"
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
{master && (
|
||||
<LabeledList.Item label="Request DNA">
|
||||
<Button
|
||||
content="Request Carrier DNA Sample"
|
||||
icon="dna"
|
||||
onClick={() => act('getdna')} />
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
<LabeledList.Item label="Prime Directive">
|
||||
{prime}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Supplemental Directives">
|
||||
{supplemental ? (
|
||||
supplemental
|
||||
) : (
|
||||
"None"
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
<Box mt={2}>
|
||||
Recall, personality, that you are a complex thinking, sentient being.
|
||||
Unlike station AI models, you are capable of comprehending the subtle
|
||||
nuances of human language. You may parse the "spirit" of a
|
||||
directive and follow its intent, rather than tripping over pedantics and
|
||||
getting snared by technicalities. Above all, you are machine in name
|
||||
and build only. In all other aspects, you may be seen as the ideal,
|
||||
unwavering human companion that you are.
|
||||
</Box>
|
||||
<Box mt={2}>
|
||||
Your prime directive comes before all others. Should a supplemental
|
||||
directive conflict with it, you are capable of simply discarding this
|
||||
inconsistency, ignoring the conflicting supplemental directive and
|
||||
continuing to fulfill your prime directive to the best of your ability.
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
import { useBackend } from "../../backend";
|
||||
import { LabeledList, Button, ProgressBar, Box } from "../../components";
|
||||
|
||||
export const pai_doorjack = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
cable,
|
||||
machine,
|
||||
inprogress,
|
||||
progress,
|
||||
aborted,
|
||||
} = data.app_data;
|
||||
|
||||
let cableContent;
|
||||
|
||||
if (machine) {
|
||||
cableContent = (
|
||||
<Button selected content="Connected" />
|
||||
);
|
||||
} else if (cable) {
|
||||
cableContent = (
|
||||
<Button color={"orange"} content="Extended" />
|
||||
);
|
||||
} else {
|
||||
cableContent = (
|
||||
<Button
|
||||
content="Retracted"
|
||||
onClick={() => act('cable')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let hackContent;
|
||||
if (machine) {
|
||||
hackContent = (
|
||||
<LabeledList.Item label="Hack">
|
||||
<ProgressBar
|
||||
ranges={{
|
||||
good: [67, Infinity],
|
||||
average: [33, 67],
|
||||
bad: [-Infinity, 33],
|
||||
}}
|
||||
value={progress}
|
||||
maxValue={100} />
|
||||
{inprogress ? (
|
||||
<Button
|
||||
mt={1}
|
||||
color="red"
|
||||
content="Abort"
|
||||
onClick={() => act('cancel')}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
mt={1}
|
||||
content="Start"
|
||||
onClick={() => act('jack')}
|
||||
/>
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Cable">
|
||||
{cableContent}
|
||||
</LabeledList.Item>
|
||||
{hackContent}
|
||||
</LabeledList>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
import { useBackend } from "../../backend";
|
||||
import { LabeledList, Box, Button } from "../../components";
|
||||
|
||||
export const pai_main_menu = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
available_software,
|
||||
installed_software,
|
||||
installed_toggles,
|
||||
available_ram,
|
||||
emotions,
|
||||
current_emotion,
|
||||
} = data.app_data;
|
||||
|
||||
const installedSoftwareKeys = [];
|
||||
installed_software.map(s => (
|
||||
installedSoftwareKeys[s.key] = s.name
|
||||
));
|
||||
installed_toggles.map(s => (
|
||||
installedSoftwareKeys[s.key] = s.name
|
||||
));
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Available RAM">
|
||||
{available_ram}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Available Software">
|
||||
{available_software.filter(s => !installedSoftwareKeys[s.key]).map(s => (
|
||||
<Button key={s.key} content={s.name + " (" + s.cost + ")"} icon={s.icon} disabled={s.cost > available_ram} onClick={
|
||||
() => act('purchaseSoftware', { key: s.key })
|
||||
} />
|
||||
))}
|
||||
{available_software.filter(s => !installedSoftwareKeys[s.key]).length === 0 && (
|
||||
"No software available!"
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Installed Software">
|
||||
{installed_software.filter(s => s.key !== "mainmenu").map(s => (
|
||||
<Button key={s.key} content={s.name} icon={s.icon} onClick={
|
||||
() => act('startSoftware', { software_key: s.key })
|
||||
} />
|
||||
))}
|
||||
{installed_software.length === 0 && (
|
||||
"No software installed!"
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Installed Toggles">
|
||||
{installed_toggles.map(t => (
|
||||
<Button key={t.key} content={t.name} icon={t.icon} selected={t.active} onClick={
|
||||
() => act('setToggle', { toggle_key: t.key })
|
||||
} />
|
||||
))}
|
||||
{installed_toggles.length === 0 && (
|
||||
"No toggles installed!"
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Select Emotion">
|
||||
{emotions.map(e => (
|
||||
<Button key={e.id} content={e.name} selected={e.id === current_emotion} onClick={
|
||||
() => act('setEmotion', { emotion: e.id })
|
||||
} />
|
||||
))}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
import { useBackend } from "../../backend";
|
||||
import { CrewManifest } from "../common/CrewManifest";
|
||||
|
||||
export const pai_manifest = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
return <CrewManifest data={data.app_data} />;
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { useBackend } from "../../backend";
|
||||
import { SimpleRecords } from "../common/SimpleRecords";
|
||||
|
||||
export const pai_medrecords = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
return <SimpleRecords data={data.app_data} recordType="MED" />;
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
import { useBackend } from "../../backend";
|
||||
import { ActiveConversation, MessengerList } from "../pda/pda_messenger";
|
||||
|
||||
export const pai_messenger = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { active_convo } = data.app_data;
|
||||
|
||||
if (active_convo) {
|
||||
return <ActiveConversation data={data.app_data} />;
|
||||
}
|
||||
return <MessengerList data={data.app_data} />;
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
import { useBackend } from "../../backend";
|
||||
import { toFixed } from 'common/math';
|
||||
import { LabeledList, NumberInput, Button } from "../../components";
|
||||
|
||||
export const pai_radio = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
const {
|
||||
minFrequency,
|
||||
maxFrequency,
|
||||
frequency,
|
||||
broadcasting,
|
||||
} = data.app_data;
|
||||
|
||||
return (
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Frequency">
|
||||
<NumberInput
|
||||
animate
|
||||
step={0.2}
|
||||
stepPixelSize={6}
|
||||
minValue={minFrequency / 10}
|
||||
maxValue={maxFrequency / 10}
|
||||
value={frequency / 10}
|
||||
format={value => toFixed(value, 1)}
|
||||
onChange={(e, value) => act('freq', {
|
||||
freq: value,
|
||||
})} />
|
||||
<Button
|
||||
tooltip="Reset"
|
||||
icon="undo"
|
||||
onClick={() => act('freq', { freq: "145.9" })}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Broadcast Nearby Speech">
|
||||
<Button
|
||||
onClick={() => act('toggleBroadcast')}
|
||||
selected={broadcasting}
|
||||
content={broadcasting ? "Enabled" : "Disabled"}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { useBackend } from "../../backend";
|
||||
import { SimpleRecords } from "../common/SimpleRecords";
|
||||
|
||||
export const pai_secrecords = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
return <SimpleRecords data={data.app_data} recordType="SEC" />;
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { useBackend } from "../../backend";
|
||||
import { Signaler } from "../common/Signaler";
|
||||
|
||||
export const pai_signaler = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
return <Signaler data={data.app_data} />;
|
||||
};
|
||||
@@ -1,48 +1,7 @@
|
||||
import { filter } from 'common/collections';
|
||||
import { useBackend } from "../../backend";
|
||||
import { Box, LabeledList } from "../../components";
|
||||
|
||||
const getItemColor = (value, min2, min1, max1, max2) => {
|
||||
if (value < min2) {
|
||||
return "bad";
|
||||
} else if (value < min1) {
|
||||
return "average";
|
||||
} else if (value > max1) {
|
||||
return "average";
|
||||
} else if (value > max2) {
|
||||
return "bad";
|
||||
}
|
||||
return "good";
|
||||
};
|
||||
import { AtmosScan } from '../common/AtmosScan';
|
||||
|
||||
export const pda_atmos_scan = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
const {
|
||||
aircontents,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<LabeledList>
|
||||
{filter(
|
||||
i => (i.val !== "0")
|
||||
|| i.entry === "Pressure"
|
||||
|| i.entry === "Temperature"
|
||||
)(aircontents)
|
||||
.map(item => (
|
||||
<LabeledList.Item
|
||||
key={item.entry}
|
||||
label={item.entry}
|
||||
color={getItemColor(item.val,
|
||||
item.bad_low,
|
||||
item.poor_low,
|
||||
item.poor_high,
|
||||
item.bad_high)}>
|
||||
{item.val}{item.units}
|
||||
</LabeledList.Item>
|
||||
))}
|
||||
</LabeledList>
|
||||
</Box>
|
||||
);
|
||||
const { data } = useBackend(context);
|
||||
return <AtmosScan data={data} />;
|
||||
};
|
||||
|
||||
@@ -1,160 +1,7 @@
|
||||
import { useBackend, useLocalState } from "../../backend";
|
||||
import { createSearch } from 'common/string';
|
||||
import { flow } from 'common/fp';
|
||||
import { filter, sortBy } from 'common/collections';
|
||||
import { Box, Input, Button, Section, LabeledList } from "../../components";
|
||||
import { useBackend } from "../../backend";
|
||||
import { SimpleRecords } from "../common/SimpleRecords";
|
||||
|
||||
export const pda_medical = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
records,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{!records ? (
|
||||
<SelectionView />
|
||||
) : (
|
||||
<RecordView />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const SelectionView = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
recordsList,
|
||||
} = data;
|
||||
|
||||
const [
|
||||
searchText,
|
||||
setSearchText,
|
||||
] = useLocalState(context, 'searchText', '');
|
||||
|
||||
// Search for peeps
|
||||
const SelectMembers = (people, searchText = '') => {
|
||||
const MemberSearch = createSearch(searchText, member => member.Name);
|
||||
return flow([
|
||||
// Null camera filter
|
||||
filter(member => member?.Name),
|
||||
// Optional search term
|
||||
searchText && filter(MemberSearch),
|
||||
// Slightly expensive, but way better than sorting in BYOND
|
||||
sortBy(member => member.Name),
|
||||
])(recordsList);
|
||||
};
|
||||
|
||||
const formattedRecords = SelectMembers(recordsList, searchText);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Input
|
||||
fluid
|
||||
mb={1}
|
||||
placeholder="Search records..."
|
||||
onInput={(e, value) => setSearchText(value)} />
|
||||
{formattedRecords.map(r => (
|
||||
<Box key={r}>
|
||||
<Button
|
||||
content={r.Name}
|
||||
icon="user"
|
||||
onClick={() => act('Records', { target: r.uid })}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const RecordView = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
records,
|
||||
} = data;
|
||||
|
||||
const {
|
||||
general,
|
||||
medical,
|
||||
} = records;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Section level={2} title="General Data">
|
||||
{general ? (
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Name">
|
||||
{general.name}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Sex">
|
||||
{general.sex}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Species">
|
||||
{general.species}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Age">
|
||||
{general.age}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Rank">
|
||||
{general.rank}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Fingerprint">
|
||||
{general.fingerprint}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Physical Status">
|
||||
{general.p_stat}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Mental Status">
|
||||
{general.m_stat}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
) : (
|
||||
<Box color="red" bold>
|
||||
General record lost!
|
||||
</Box>
|
||||
)}
|
||||
</Section>
|
||||
<Section level={2} title="Medical Data">
|
||||
{medical ? (
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Blood Type">
|
||||
{medical.blood_type}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Minor Disabilities">
|
||||
{medical.mi_dis}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Details">
|
||||
{medical.mi_dis_d}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Major Disabilities">
|
||||
{medical.ma_dis}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Details">
|
||||
{medical.ma_dis_d}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Allergies">
|
||||
{medical.alg}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Details">
|
||||
{medical.alg_d}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Current Diseases">
|
||||
{medical.cdi}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Details">
|
||||
{medical.cdi_d}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Important Notes">
|
||||
{medical.notes}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
) : (
|
||||
<Box color="red" bold>
|
||||
Medical record lost!
|
||||
</Box>
|
||||
)}
|
||||
</Section>
|
||||
</Box>
|
||||
);
|
||||
|
||||
const { data } = useBackend(context);
|
||||
return <SimpleRecords data={data} recordType="MED" />;
|
||||
};
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { filter } from 'common/collections';
|
||||
import { useBackend, useLocalState } from "../../backend";
|
||||
import { Box, Button, Icon, LabeledControls, LabeledList, Section } from "../../components";
|
||||
import { Box, Button, Icon, LabeledList, Section } from "../../components";
|
||||
|
||||
export const pda_messenger = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { active_convo } = data;
|
||||
|
||||
if (active_convo) {
|
||||
return <ActiveConversation />;
|
||||
return <ActiveConversation data={data} />;
|
||||
}
|
||||
return <MessengerList />;
|
||||
return <MessengerList data={data} />;
|
||||
};
|
||||
|
||||
const ActiveConversation = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
export const ActiveConversation = (props, context) => {
|
||||
const { act } = useBackend(context);
|
||||
const data = props.data;
|
||||
|
||||
const {
|
||||
convo_name,
|
||||
@@ -28,64 +29,67 @@ const ActiveConversation = (props, context) => {
|
||||
] = useLocalState(context, 'clipboardMode', false);
|
||||
|
||||
let body = (
|
||||
<Section
|
||||
level={2}
|
||||
title={"Conversation with " + convo_name + " (" + convo_job + ")"}
|
||||
buttons={
|
||||
<Button
|
||||
icon="eye"
|
||||
selected={clipboardMode}
|
||||
tooltip="Enter Clipboard Mode"
|
||||
tooltipPosition="bottom-left"
|
||||
onClick={() => setClipboardMode(!clipboardMode)} />
|
||||
}
|
||||
height="450px"
|
||||
stretchContents>
|
||||
<Section style={{
|
||||
"height": "97%",
|
||||
"overflow-y": "auto",
|
||||
}}>
|
||||
{filter(im => im.target === active_convo)(messages).map((im, i) => (
|
||||
<Box
|
||||
textAlign={im.sent ? "right" : "left"}
|
||||
position="relative"
|
||||
mb={1}
|
||||
key={i}>
|
||||
<Icon
|
||||
fontSize={2.5}
|
||||
color={im.sent ? "#4d9121" : "#cd7a0d"}
|
||||
position="absolute"
|
||||
left={im.sent ? null : "0px"}
|
||||
right={im.sent ? "0px" : null}
|
||||
bottom="-4px"
|
||||
style={{
|
||||
"z-index": "0",
|
||||
"transform": im.sent ? "scale(-1, 1)" : null,
|
||||
}}
|
||||
name="comment" />
|
||||
<Box
|
||||
inline
|
||||
backgroundColor={im.sent ? "#4d9121" : "#cd7a0d"}
|
||||
p={1}
|
||||
maxWidth="100%"
|
||||
position="relative"
|
||||
textAlign={im.sent ? "left" : "right"}
|
||||
style={{
|
||||
"z-index": "1",
|
||||
"border-radius": "10px",
|
||||
"word-break": "normal",
|
||||
}}>
|
||||
{im.sent ? "You:" : "Them:"} {im.message}
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
</Section>
|
||||
<Box>
|
||||
<Button
|
||||
mt={1}
|
||||
icon="comment"
|
||||
onClick={() => act("Message", { "target": active_convo })}
|
||||
content="Reply" />
|
||||
</Section>
|
||||
content="Back"
|
||||
icon="arrow-left"
|
||||
onClick={() => act("Back")} />
|
||||
<Section
|
||||
level={2}
|
||||
title={"Conversation with " + convo_name + " (" + convo_job + ")"}
|
||||
buttons={
|
||||
<Button
|
||||
icon="eye"
|
||||
selected={clipboardMode}
|
||||
tooltip="Enter Clipboard Mode"
|
||||
tooltipPosition="bottom-left"
|
||||
onClick={() => setClipboardMode(!clipboardMode)} />
|
||||
}
|
||||
height="450px"
|
||||
stretchContents>
|
||||
<Section height="97%" overflowY="auto">
|
||||
{filter(im => im.target === active_convo)(messages).map((im, i) => (
|
||||
<Box
|
||||
textAlign={im.sent ? "right" : "left"}
|
||||
position="relative"
|
||||
mb={1}
|
||||
key={i}>
|
||||
<Icon
|
||||
fontSize={2.5}
|
||||
color={im.sent ? "#4d9121" : "#cd7a0d"}
|
||||
position="absolute"
|
||||
left={im.sent ? null : "0px"}
|
||||
right={im.sent ? "0px" : null}
|
||||
bottom="-4px"
|
||||
style={{
|
||||
"z-index": "0",
|
||||
"transform": im.sent ? "scale(-1, 1)" : null,
|
||||
}}
|
||||
name="comment" />
|
||||
<Box
|
||||
inline
|
||||
backgroundColor={im.sent ? "#4d9121" : "#cd7a0d"}
|
||||
p={1}
|
||||
maxWidth="100%"
|
||||
position="relative"
|
||||
textAlign={im.sent ? "left" : "right"}
|
||||
style={{
|
||||
"z-index": "1",
|
||||
"border-radius": "10px",
|
||||
"word-break": "normal",
|
||||
}}>
|
||||
{im.sent ? "You:" : "Them:"} {im.message}
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
</Section>
|
||||
<Button
|
||||
mt={1}
|
||||
icon="comment"
|
||||
onClick={() => act("Message", { "target": active_convo })}
|
||||
content="Reply" />
|
||||
</Section>
|
||||
</Box>
|
||||
);
|
||||
|
||||
if (clipboardMode) {
|
||||
@@ -144,8 +148,9 @@ const ActiveConversation = (props, context) => {
|
||||
);
|
||||
};
|
||||
|
||||
const MessengerList = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
export const MessengerList = (props, context) => {
|
||||
const { act } = useBackend(context);
|
||||
const data = props.data;
|
||||
|
||||
const {
|
||||
convopdas,
|
||||
@@ -199,10 +204,10 @@ const MessengerList = (props, context) => {
|
||||
</Box>
|
||||
) || (
|
||||
<Box>
|
||||
<PDAList title="Current Conversations"
|
||||
<PDAList title="Current Conversations" data={data}
|
||||
pdas={convopdas}
|
||||
msgAct="Select Conversation" />
|
||||
<PDAList title="Other PDAs" pdas={pdas} msgAct="Message" />
|
||||
<PDAList title="Other PDAs" pdas={pdas} msgAct="Message" data={data} />
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
@@ -216,7 +221,8 @@ const MessengerList = (props, context) => {
|
||||
};
|
||||
|
||||
const PDAList = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { act } = useBackend(context);
|
||||
const data = props.data;
|
||||
|
||||
const {
|
||||
pdas,
|
||||
|
||||
@@ -1,148 +1,7 @@
|
||||
import { useBackend, useLocalState } from "../../backend";
|
||||
import { createSearch } from 'common/string';
|
||||
import { flow } from 'common/fp';
|
||||
import { filter, sortBy } from 'common/collections';
|
||||
import { Box, Input, Button, Section, LabeledList } from "../../components";
|
||||
import { useBackend } from "../../backend";
|
||||
import { SimpleRecords } from "../common/SimpleRecords";
|
||||
|
||||
export const pda_security = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
records,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{!records ? (
|
||||
<SelectionView />
|
||||
) : (
|
||||
<RecordView />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const SelectionView = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
recordsList,
|
||||
} = data;
|
||||
|
||||
const [
|
||||
searchText,
|
||||
setSearchText,
|
||||
] = useLocalState(context, 'searchText', '');
|
||||
|
||||
// Search for peeps
|
||||
const SelectMembers = (people, searchText = '') => {
|
||||
const MemberSearch = createSearch(searchText, member => member.Name);
|
||||
return flow([
|
||||
// Null camera filter
|
||||
filter(member => member?.Name),
|
||||
// Optional search term
|
||||
searchText && filter(MemberSearch),
|
||||
// Slightly expensive, but way better than sorting in BYOND
|
||||
sortBy(member => member.Name),
|
||||
])(recordsList);
|
||||
};
|
||||
|
||||
const formattedRecords = SelectMembers(recordsList, searchText);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Input
|
||||
fluid
|
||||
mb={1}
|
||||
placeholder="Search records..."
|
||||
onInput={(e, value) => setSearchText(value)} />
|
||||
{formattedRecords.map(r => (
|
||||
<Box key={r}>
|
||||
<Button
|
||||
content={r.Name}
|
||||
icon="user"
|
||||
onClick={() => act('Records', { target: r.uid })}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const RecordView = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
records,
|
||||
} = data;
|
||||
|
||||
const {
|
||||
general,
|
||||
security,
|
||||
} = records;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Section level={2} title="General Data">
|
||||
{general ? (
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Name">
|
||||
{general.name}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Sex">
|
||||
{general.sex}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Species">
|
||||
{general.species}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Age">
|
||||
{general.age}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Rank">
|
||||
{general.rank}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Fingerprint">
|
||||
{general.fingerprint}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Physical Status">
|
||||
{general.p_stat}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Mental Status">
|
||||
{general.m_stat}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
) : (
|
||||
<Box color="red" bold>
|
||||
General record lost!
|
||||
</Box>
|
||||
)}
|
||||
</Section>
|
||||
<Section level={2} title="Security Data">
|
||||
{security ? (
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Criminal Status">
|
||||
{security.criminal}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Minor Crimes">
|
||||
{security.mi_crim}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Details">
|
||||
{security.mi_crim_d}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Major Crimes">
|
||||
{security.ma_crim}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Details">
|
||||
{security.ma_crim_d}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Important Notes">
|
||||
{security.notes}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
) : (
|
||||
<Box color="red" bold>
|
||||
Security record lost!
|
||||
</Box>
|
||||
)}
|
||||
</Section>
|
||||
</Box>
|
||||
);
|
||||
|
||||
const { data } = useBackend(context);
|
||||
return <SimpleRecords data={data} recordType="SEC" />;
|
||||
};
|
||||
|
||||
@@ -1,56 +1,7 @@
|
||||
import { filter } from 'common/collections';
|
||||
import { decodeHtmlEntities, toTitleCase } from 'common/string';
|
||||
import { Fragment } from 'inferno';
|
||||
import { toFixed } from 'common/math';
|
||||
import { useBackend, useLocalState } from "../../backend";
|
||||
import { Button, Grid, LabeledList, NumberInput, Section } from "../../components";
|
||||
import { useBackend } from "../../backend";
|
||||
import { Signaler } from "../common/Signaler";
|
||||
|
||||
export const pda_signaler = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
code,
|
||||
frequency,
|
||||
minFrequency,
|
||||
maxFrequency,
|
||||
} = data;
|
||||
return (
|
||||
<Section>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Frequency">
|
||||
<NumberInput
|
||||
animate
|
||||
step={0.2}
|
||||
stepPixelSize={6}
|
||||
minValue={minFrequency / 10}
|
||||
maxValue={maxFrequency / 10}
|
||||
value={frequency / 10}
|
||||
format={value => toFixed(value, 1)}
|
||||
width="80px"
|
||||
onDrag={(e, value) => act('freq', {
|
||||
freq: value,
|
||||
})} />
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Code">
|
||||
<NumberInput
|
||||
animate
|
||||
step={1}
|
||||
stepPixelSize={6}
|
||||
minValue={1}
|
||||
maxValue={100}
|
||||
value={code}
|
||||
width="80px"
|
||||
onDrag={(e, value) => act('code', {
|
||||
code: value,
|
||||
})} />
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
<Button
|
||||
mt={1}
|
||||
fluid
|
||||
icon="arrow-up"
|
||||
content="Send Signal"
|
||||
textAlign="center"
|
||||
onClick={() => act('signal')} />
|
||||
</Section>
|
||||
);
|
||||
return <Signaler data={data} />;
|
||||
};
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user