pAIs, Computers and ntIRC (#8757)

#8005 just revived.

Tasks to acomplish while here:

Fix merge conflicts

    Add ntIRC direct messaging

Things I migth consider adding in future PR:

    Porting IRC to VueUI
    Porting File Manager to VueUI.
This commit is contained in:
Karolis
2020-05-08 13:58:34 +03:00
committed by GitHub
parent 691a11cf00
commit 1c00ae02d3
72 changed files with 6149 additions and 4768 deletions

View File

@@ -182,10 +182,14 @@
#define PROGRAM_LAPTOP 2
#define PROGRAM_TABLET 4
#define PROGRAM_TELESCREEN 8
#define PROGRAM_SILICON 16
#define PROGRAM_SILICON_AI 16
#define PROGRAM_WRISTBOUND 32
#define PROGRAM_SILICON_ROBOT 64
#define PROGRAM_SILICON_PAI 128
#define PROGRAM_ALL (PROGRAM_CONSOLE | PROGRAM_LAPTOP | PROGRAM_TABLET | PROGRAM_WRISTBOUND | PROGRAM_TELESCREEN | PROGRAM_SILICON)
#define PROGRAM_ALL (PROGRAM_CONSOLE | PROGRAM_LAPTOP | PROGRAM_TABLET | PROGRAM_WRISTBOUND | PROGRAM_TELESCREEN | PROGRAM_SILICON_AI | PROGRAM_SILICON_ROBOT | PROGRAM_SILICON_PAI)
#define PROGRAM_SILICON (PROGRAM_SILICON_AI | PROGRAM_SILICON_ROBOT | PROGRAM_SILICON_PAI)
#define PROGRAM_STATIONBOUND (PROGRAM_SILICON_AI | PROGRAM_SILICON_ROBOT)
#define PROGRAM_ALL_REGULAR (PROGRAM_CONSOLE | PROGRAM_LAPTOP | PROGRAM_TABLET | PROGRAM_WRISTBOUND | PROGRAM_TELESCREEN)
#define PROGRAM_STATE_KILLED 0
@@ -200,6 +204,10 @@
#define PROGRAM_ACCESS_LIST_ONE 2
#define PROGRAM_ACCESS_LIST_ALL 3
#define PROGRAM_NORMAL 1
#define PROGRAM_SERVICE 2
#define PROGRAM_TYPE_ALL (PROGRAM_NORMAL | PROGRAM_SERVICE)
// Special return values from bullet_act(). Positive return values are already used to indicate the blocked level of the projectile.
#define PROJECTILE_CONTINUE -1 //if the projectile should continue flying after calling bullet_act()
#define PROJECTILE_FORCE_MISS -2 //if the projectile should treat the attack as a miss (suppresses attack and admin logs) - only applies to mobs.

View File

@@ -98,7 +98,7 @@
/datum/controller/subsystem/ghostroles/proc/vui_interact(mob/user,var/spawnpoint=null)
var/datum/vueui/ui = SSvueui.get_open_ui(user,src)
if(!ui)
ui = new(user,src,"misc-ghostspawner",950,700,"Ghost Role Spawner", nstate = interactive_state)
ui = new(user,src,"misc-ghostspawner",950,700,"Ghost Role Spawner", state = interactive_state)
ui.data = vueui_data_change(list("spawnpoint"=spawnpoint,"current_tag"="All"),user,ui)
ui.open()

View File

@@ -20,18 +20,6 @@
LAZYINITLIST(pai_software_by_key)
LAZYINITLIST(default_pai_software)
/datum/controller/subsystem/pai/Initialize()
// Initialize the pAI software list.
for(var/type in subtypesof(/datum/pai_software))
var/datum/pai_software/P = new type()
if(pai_software_by_key[P.id])
var/datum/pai_software/O = pai_software_by_key[P.id]
to_world("<span class='warning'>pAI software module [P.name] has the same key as [O.name]!</span>")
continue
pai_software_by_key[P.id] = P
if(P.default)
default_pai_software[P.id] = P
/datum/controller/subsystem/pai/Recover()
pai_software_by_key = SSpai.pai_software_by_key
default_pai_software = SSpai.default_pai_software
@@ -141,7 +129,7 @@
"description" = candidate.description,
"role" = candidate.role,
"comments" = candidate.comments),
nstate = interactive_state)
state = interactive_state)
ui.metadata = list("candidate" = candidate)
ui.header = "minimal"

View File

@@ -187,6 +187,7 @@ Byond Vue UI framework's management subsystem
LAZYINITLIST(open_uis[new_object_key])
for(var/datum/vueui/ui in open_uis[old_object_key])
ui.object.vueui_on_transfer(ui)
ui.object = new_object
if(new_activeui) {ui.activeui = new_activeui}
if(new_title) {ui.title = new_title}

View File

@@ -216,9 +216,9 @@
dat = replacetext(dat, "\t", "")
return dat
/datum/controller/subsystem/records/proc/get_manifest_json()
/datum/controller/subsystem/records/proc/get_manifest_list()
if(manifest.len)
return manifest_json
return manifest
manifest = list(
"heads" = list(),
"sec" = list(),
@@ -256,6 +256,7 @@
department = 1
if ((depthead || rank == "Captain") && manifest[positionType].len != 1)
manifest[positionType].Swap(1, manifest[positionType].len)
manifest[positionType][1]["head"] = TRUE
if(positionType == "head")
depthead = 1
@@ -278,6 +279,13 @@
manifest["bot"].Swap(1, manifest["bot"].len)
manifest_json = json_encode(manifest)
return manifest
/datum/controller/subsystem/records/proc/get_manifest_json()
if(manifest.len)
return manifest_json
get_manifest_list()
return manifest_json
/datum/controller/subsystem/records/proc/onDelete(var/datum/record/r)

View File

@@ -398,7 +398,7 @@ var/datum/controller/subsystem/vote/SSvote
/datum/controller/subsystem/vote/proc/OpenVotingUI(var/mob/user)
var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
if (!ui)
ui = new(user, SSvote, "misc-voting", 400, 500, "Voting panel", nstate = interactive_state)
ui = new(user, SSvote, "misc-voting", 400, 500, "Voting panel", state = interactive_state)
ui.header = "minimal"
ui.open()

View File

@@ -13,6 +13,7 @@
weakref = null
destroyed_event.raise_event(src)
SSnanoui.close_uis(src)
SSvueui.close_uis(src)
tag = null
var/list/timers = active_timers
active_timers = null

View File

@@ -38,7 +38,7 @@ var/datum/vueui_module/player_panel/global_player_panel
return
var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
if(!ui)
ui = new(user, src, "admin-player-panel", 800, 600, "Modern player panel", nstate = interactive_state)
ui = new(user, src, "admin-player-panel", 800, 600, "Modern player panel", state = interactive_state)
ui.header = "minimal"
ui.auto_update_content = TRUE

View File

@@ -19,7 +19,6 @@
return ..()
/mob/living/silicon/proc/init_subsystems()
computer = new/obj/item/modular_computer/silicon/preset(src)
alarm_monitor = new(src)
law_manager = new(src)
rcon = new(src)
@@ -33,8 +32,17 @@
/mob/living/silicon/ai/init_subsystems()
..()
computer = new/obj/item/modular_computer/silicon/ai(src)
ntnet_monitor = new(src)
/mob/living/silicon/pai/init_subsystems()
..()
computer = new/obj/item/modular_computer/silicon/pai(src)
/mob/living/silicon/robot/init_subsystems()
..()
computer = new/obj/item/modular_computer/silicon/robot(src)
/****************
* Computer *
*****************/

View File

@@ -10,6 +10,7 @@
mob_size = 1//As a holographic projection, a pAI is massless except for its card device
can_pull_size = 2 //max size for an object the pAI can pull
var/network = "SS13"
var/obj/machinery/camera/current = null
var/ram = 100 // Used as currency to purchase different abilities
@@ -46,6 +47,24 @@
"Rodent" = list("squeaks","squeals","squeeks")
)
var/list/pai_emotions = list(
"Happy" = 1,
"Cat" = 2,
"Extremely Happy" = 3,
"Face" = 4,
"Laugh" = 5,
"Off" = 6,
"Sad" = 7,
"Angry" = 8,
"What" = 9,
"Neutral" = 10,
"Silly" = 11,
"Nose" = 12,
"Smirk" = 13,
"Exclamation Points" = 14,
"Question Mark" = 15
)
var/obj/item/pai_cable/cable // The cable we produce and use when door or camera jacking
id_card_type = /obj/item/card/id //Internal ID used to store copied owner access, and to check access for airlocks
@@ -68,11 +87,6 @@
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/record/general/active // Datacore record declarations for record software
var/security_cannotfind = 0
var/obj/machinery/door/airlock/hackdoor // The airlock being hacked
var/hackprogress = 0 // Possible values: 0 - 1000, >= 1000 means the hack is complete and will be reset upon next check
var/hack_aborted = 0
@@ -136,10 +150,12 @@
add_language(LANGUAGE_TRADEBAND, 1)
add_language(LANGUAGE_GUTTER, 1)
add_language(LANGUAGE_EAL, 1)
add_language(LANGUAGE_SIGN, 0)
set_custom_sprite()
verbs += /mob/living/silicon/pai/proc/choose_chassis
verbs += /mob/living/silicon/pai/proc/choose_verbs
verbs += /mob/living/silicon/proc/computer_interact
//PDA
pda = new(src)
@@ -245,16 +261,6 @@
src.reset_view(C)
return 1
/mob/living/silicon/pai/verb/reset_record_view()
set category = "pAI Commands"
set name = "Reset Records Software"
active = null
security_cannotfind = 0
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"
@@ -509,3 +515,14 @@
else
to_chat(src, "<span class='warning'>You are too small to pull that.</span>")
return
/mob/living/silicon/pai/verb/select_card_icon()
set category = "pAI Commands"
set name = "Select card icon"
if(stat || sleeping || paralysis || weakened)
return
var/selection = input(src, "Choose an icon for you card.") in pai_emotions
card.setEmotion(pai_emotions[selection])

View File

@@ -1,116 +0,0 @@
var/list/pai_emotions = list(
"Happy" = 1,
"Cat" = 2,
"Extremely Happy" = 3,
"Face" = 4,
"Laugh" = 5,
"Off" = 6,
"Sad" = 7,
"Angry" = 8,
"What" = 9,
"Neutral" = 10,
"Silly" = 11,
"Nose" = 12,
"Smirk" = 13,
"Exclamation Points" = 14,
"Question Mark" = 15
)
/mob/living/silicon/pai/Initialize()
..()
software = SSpai.default_pai_software.Copy()
/mob/living/silicon/pai/verb/paiInterface()
set category = "pAI Commands"
set name = "Software Interface"
ui_interact(src)
/mob/living/silicon/pai/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
if(user != src)
if(ui) ui.set_status(STATUS_CLOSE, 0)
return
if(ui_key != "main")
var/datum/pai_software/S = software[ui_key]
if(S && !S.toggle)
S.on_ui_interact(src, ui, force_open)
else
if(ui) ui.set_status(STATUS_CLOSE, 0)
return
var/data[0]
// Software we have bought
var/bought_software[0]
// Software we have not bought
var/not_bought_software[0]
var/list/pai_software_by_key = SSpai.pai_software_by_key
for(var/key in pai_software_by_key)
var/datum/pai_software/S = 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 pai_emotions)
var/emote[0]
emote["name"] = name
emote["id"] = pai_emotions[name]
emotions[++emotions.len] = emote
data["emotions"] = emotions
data["current_emotion"] = card.current_emotion
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "pai_interface.tmpl", "pAI Software Interface", 450, 600)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
/mob/living/silicon/pai/Topic(href, href_list)
. = ..()
if(.) return
if(href_list["software"])
var/soft = href_list["software"]
var/datum/pai_software/S = software[soft]
if(S.toggle)
S.toggle(src)
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 = SSpai.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 <= 15)
card.setEmotion(img)
return 1

View File

@@ -1,509 +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_interact() and Topic()
var/toggle = 1
// Whether pAIs should automatically receive this module at no cost
var/default = 0
proc/on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1)
return
proc/toggle(mob/living/silicon/pai/user)
return
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
on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1)
var/data[0]
data["master"] = user.master
data["dna"] = user.master_dna
data["prime"] = user.pai_law0
data["supplemental"] = user.pai_laws
ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open)
if(!ui)
// Don't copy-paste this unless you're making a pAI software module!
ui = new(user, user, id, "pai_directives.tmpl", "pAI Directives", 450, 600)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
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(src, "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 \his 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 \he [gender_datums[M.gender].is] 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
on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui = null, force_open = 1)
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/device/radio
ch_dat["listening"] = !!(ch_stat & user.radio.FREQ_LISTENING)
channels[++channels.len] = ch_dat
data["channels"] = channels
ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open)
if(!ui)
ui = new(user, user, id, "pai_radio.tmpl", "Radio Configuration", 300, 150)
ui.set_initial_data(data)
ui.open()
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
on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1)
var/data[0]
// This is dumb, but NanoUI breaks if it has no data to send
data["manifest"] = list("__json_cache" = SSrecords.get_manifest_json())
ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open)
if(!ui)
// Don't copy-paste this unless you're making a pAI software module!
ui = new(user, user, id, "pai_manifest.tmpl", "Crew Manifest", 450, 600)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
/datum/pai_software/messenger
name = "Digital Messenger"
ram_cost = 5
id = "messenger"
toggle = 0
on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1)
var/data[0]
data["receiver_off"] = user.pda.toff
data["ringer_off"] = user.pda.message_silent
data["current_ref"] = null
data["current_name"] = user.current_pda_messaging
var/pdas[0]
if(!user.pda.toff)
for(var/obj/item/device/pda/P in sortAtom(PDAs))
if(!P.owner || P.toff || P == user.pda || P.hidden) 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 user.pda.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
ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open)
if(!ui)
// Don't copy-paste this unless you're making a pAI software module!
ui = new(user, user, id, "pai_messenger.tmpl", "Digital Messenger", 450, 600)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
Topic(href, href_list)
var/mob/living/silicon/pai/P = usr
if(!istype(P)) return
if(!isnull(P.pda))
if(href_list["toggler"])
P.pda.toff = href_list["toggler"] != "1"
return 1
else if(href_list["ringer"])
P.pda.message_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"])
P.pda.create_message(P, target, 0)
return 1
/datum/pai_software/med_records
name = "Medical Records"
ram_cost = 15
id = "med_records"
toggle = 0
on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1)
var/data[0]
var/records[0]
for(var/datum/record/general/R in sortRecord(SSrecords.records))
var/record[0]
record["name"] = R.name
record["ref"] = "\ref[R]"
records[++records.len] = record
data["records"] = records
var/datum/record/general/R = user.active
data["general"] = R ? R.Listify(0) : null
data["medical"] = R ? R.medical.Listify(0) : null
data["could_not_find"] = user.medical_cannotfind
ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open)
if(!ui)
// Don't copy-paste this unless you're making a pAI software module!
ui = new(user, user, id, "pai_medrecords.tmpl", "Medical Records", 450, 600)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
Topic(href, href_list)
var/mob/living/silicon/pai/P = usr
if(!istype(P)) return
if(href_list["select"])
var/datum/record/general/record = locate(href_list["select"])
if(istype(record))
if(!(record in SSrecords.records))
P.active = null
P.medical_cannotfind = 1
else
P.medical_cannotfind = 0
P.active = record
else
P.medical_cannotfind = 1
return 1
/datum/pai_software/sec_records
name = "Security Records"
ram_cost = 15
id = "sec_records"
toggle = 0
on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1)
var/data[0]
var/records[0]
for(var/datum/record/general/R in sortRecord(SSrecords.records))
var/record[0]
record["name"] = R.name
record["ref"] = "\ref[R]"
records[++records.len] = record
data["records"] = records
var/datum/record/general/R = user.active
data["general"] = R ? R.Listify(0) : null
data["security"] = R ? R.security.Listify() : null
data["could_not_find"] = user.security_cannotfind
ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open)
if(!ui)
// Don't copy-paste this unless you're making a pAI software module!
ui = new(user, user, id, "pai_secrecords.tmpl", "Security Records", 450, 600)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
Topic(href, href_list)
var/mob/living/silicon/pai/P = usr
if(!istype(P)) return
if(href_list["select"])
var/datum/record/general/record = locate(href_list["select"])
if(istype(record))
if(!(record in SSrecords.records))
P.active = null
P.security_cannotfind = 1
else
P.security_cannotfind = 0
P.active = record
else
P.active = null
P.security_cannotfind = 1
return 1
/datum/pai_software/door_jack
name = "Door Jack"
ram_cost = 30
id = "door_jack"
toggle = 0
on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1)
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
ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open)
if(!ui)
// Don't copy-paste this unless you're making a pAI software module!
ui = new(user, user, id, "pai_doorjack.tmpl", "Door Jack", 300, 150)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
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/turf/T = get_turf_or_move(src.loc)
for(var/mob/living/silicon/ai/AI in player_list)
if(T.loc)
to_chat(AI, "<font color = red><b>Network Alert: Brute-force encryption crack in progress in [T.loc].</b></font>")
else
to_chat(AI, "<font color = red><b>Network Alert: Brute-force encryption crack in progress. Unable to pinpoint location.</b></font>")
var/obj/machinery/door/airlock/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.unlock() //unbolts door as long as bolt wires aren't cut
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
on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1)
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
var/pres = env.return_pressure() * 10
data["pressure"] = "[round(pres/10)].[pres%10]"
data["temperature"] = round(env.temperature)
data["temperatureC"] = round(env.temperature-T0C)
var/t_moles = env.total_moles
var/gases[0]
for(var/g in env.gas)
var/gas[0]
gas["name"] = gas_data.name[g]
gas["percent"] = round((env.gas[g] / t_moles) * 100)
gases[++gases.len] = gas
data["gas"] = gases
ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open)
if(!ui)
// Don't copy-paste this unless you're making a pAI software module!
ui = new(user, user, id, "pai_atmosphere.tmpl", "Atmosphere Sensor", 350, 300)
ui.set_initial_data(data)
ui.open()
/datum/pai_software/sec_hud
name = "Security HUD"
ram_cost = 20
id = "sec_hud"
toggle(mob/living/silicon/pai/user)
user.secHUD = !user.secHUD
is_active(mob/living/silicon/pai/user)
return user.secHUD
/datum/pai_software/med_hud
name = "Medical HUD"
ram_cost = 20
id = "med_hud"
toggle(mob/living/silicon/pai/user)
user.medHUD = !user.medHUD
is_active(mob/living/silicon/pai/user)
return user.medHUD
/datum/pai_software/translator
name = "Universal Translator"
ram_cost = 35
id = "translator"
toggle(mob/living/silicon/pai/user)
// Sol Common, Tradeband and Gutter 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(LANGUAGE_UNATHI)
user.add_language(LANGUAGE_SIIK_MAAS)
user.add_language(LANGUAGE_SKRELLIAN)
user.add_language(LANGUAGE_ROOTSONG)
else
user.remove_language(LANGUAGE_UNATHI)
user.remove_language(LANGUAGE_SIIK_MAAS)
user.remove_language(LANGUAGE_SKRELLIAN)
user.add_language(LANGUAGE_ROOTSONG)
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
on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1)
var/data[0]
data["frequency"] = format_frequency(user.sradio.frequency)
data["code"] = user.sradio.code
ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open)
if(!ui)
// Don't copy-paste this unless you're making a pAI software module!
ui = new(user, user, id, "pai_signaller.tmpl", "Signaller", 320, 150)
ui.set_initial_data(data)
ui.open()
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(text("\icon[] *beep* *beep*", P), 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

View File

@@ -5,6 +5,7 @@ var/global/ntnrc_uid = 0
var/datum/computer_file/program/chatclient/operator // "Administrator" of this channel. Creator starts as channel's operator,
var/list/messages = list()
var/list/clients = list()
var/direct = FALSE
var/password
/datum/ntnet_conversation/New(var/name, var/no_operator)
@@ -22,10 +23,8 @@ var/global/ntnrc_uid = 0
log_ntirc("[user.client.ckey]/([username]) : [message]", ckey=key_name(user), conversation=title)
for(var/datum/computer_file/program/chatclient/C in clients)
if(C.username == username || !C.computer.screen_on)
continue
if(C.computer.active_program == C || (C in C.computer.idle_threads))
C.computer.output_message(FONT_SMALL("<b>([title]) [username]:</b> [message]"), 0)
if(C.program_state > PROGRAM_STATE_KILLED && C.username != username)
C.computer.output_message(FONT_SMALL("<b>[get_title(C)]) <i>[username]</i>:</b> [message]"), 0)
message = "[worldtime2text()] [username]: [message]"
messages.Add(message)
@@ -46,16 +45,27 @@ var/global/ntnrc_uid = 0
/datum/ntnet_conversation/proc/add_client(var/datum/computer_file/program/chatclient/C)
if(!istype(C))
return
if (C in clients)
return
clients.Add(C)
// No operator, so we assume the channel was empty. Assign this user as operator.
if(!operator)
changeop(C)
for(var/datum/computer_file/program/chatclient/CC in clients)
if(CC == C || !CC.computer.screen_on)
continue
if(CC.computer.active_program == CC || (CC in CC.computer.idle_threads))
CC.computer.output_message(FONT_SMALL("<b>([title]) A new client ([C.username]) has entered the chat.</b>"), 0)
if(CC.program_state > PROGRAM_STATE_KILLED && CC != C)
if(!direct)
CC.computer.output_message(FONT_SMALL("<b>([get_title(CC)]) <i>[C.username]</i> has entered the chat.</b>"), 0)
/datum/ntnet_conversation/proc/begin_direct(var/datum/computer_file/program/chatclient/CA, var/datum/computer_file/program/chatclient/CB)
if(!istype(CA) || !istype(CB))
return
direct = TRUE
clients.Add(CA)
clients.Add(CB)
add_status_message("[CA.username] has opened direct conversation.")
if(CB.program_state > PROGRAM_STATE_KILLED)
CB.computer.output_message(FONT_SMALL("<b>([get_title(CB)]) <i>[CA.username]</i> has opened direct conversation with you.</b>"), 0)
/datum/ntnet_conversation/proc/remove_client(var/datum/computer_file/program/chatclient/C)
if(!istype(C) || !(C in clients))
@@ -70,10 +80,8 @@ var/global/ntnrc_uid = 0
changeop(newop)
for(var/datum/computer_file/program/chatclient/CC in clients)
if(CC == C || !CC.computer.screen_on)
continue
if(CC.computer.active_program == CC || (CC in CC.computer.idle_threads))
CC.computer.output_message(FONT_SMALL("<b>([title]) A client ([C.username]) has left the chat.</b>"), 0)
if(CC.program_state > PROGRAM_STATE_KILLED && CC != C)
CC.computer.output_message(FONT_SMALL("<b>([get_title(CC)]) <i>[C.username]</i> has left the chat.</b>"), 0)
/datum/ntnet_conversation/proc/changeop(var/datum/computer_file/program/chatclient/newop)
@@ -85,11 +93,29 @@ var/global/ntnrc_uid = 0
if(operator != client)
return 0 // Not Authorised
add_status_message("[client.username] has changed channel title from [title] to [newtitle]")
add_status_message("[client.username] has changed channel title from [get_title(client)] to [newtitle]")
for(var/datum/computer_file/program/chatclient/C in clients)
if(C.program_state > PROGRAM_STATE_KILLED && C != client)
C.computer.output_message(FONT_SMALL("([get_title(C)]) <i>[client.username]</i> has changed the channel title to <b>[newtitle]</b>."), 0)
title = newtitle
for(var/datum/computer_file/program/chatclient/C in clients)
if(C == client || !C.computer.screen_on)
continue
if(C.computer.active_program == src || (C in C.computer.idle_threads))
C.computer.output_message(FONT_SMALL("([title]) A new client ([C.username]) has entered the chat."), 0)
/datum/ntnet_conversation/proc/get_title(var/datum/computer_file/program/chatclient/cl = null)
if(direct)
var/names = list()
for(var/datum/computer_file/program/chatclient/C in clients)
names += C.username
if(cl)
names -= cl.username
return "\[DM] [english_list(names)]"
else
return title
/datum/ntnet_conversation/proc/can_see(var/datum/computer_file/program/chatclient/cl)
if(cl in clients)
return TRUE
if(cl.netadmin_mode)
return TRUE
if(!direct)
return TRUE
return FALSE

View File

@@ -11,6 +11,7 @@ var/global/datum/ntnet/ntnet_global = new()
var/list/available_software_presets = list()
var/list/available_news = list()
var/list/chat_channels = list()
var/list/chat_clients = list()
var/list/fileservers = list()
// Amount of logs the system tries to keep in memory. Keep below 999 to prevent byond from acting weirdly.
// High values make displaying logs much laggier.

View File

@@ -33,6 +33,15 @@
else
idle_threads.Remove(P)
for(var/s in enabled_services)
var/datum/computer_file/program/service = s
if(service.program_type & PROGRAM_SERVICE) // Safety checks
if(service.service_state == PROGRAM_STATE_ACTIVE)
if(active_program != service && !(service in idle_threads))
service.process_tick()
else
enabled_services -= service
working = hard_drive && processor_unit && damage < broken_damage && computer_use_power()
check_update_ui_need()
@@ -169,6 +178,13 @@
for(var/datum/computer_file/program/P in idle_threads)
P.kill_program(TRUE)
idle_threads.Remove(P)
for(var/s in enabled_services)
var/datum/computer_file/program/service = s
if(service.program_type & PROGRAM_SERVICE) // Safety checks
service.service_deactivate()
service.service_state = PROGRAM_STATE_KILLED
if(loud)
visible_message(SPAN_NOTICE("\The [src] shuts down."))
SSvueui.close_uis(src)
@@ -182,7 +198,13 @@
// Autorun feature
var/datum/computer_file/data/autorun = hard_drive ? hard_drive.find_file_by_name("autorun") : null
if(istype(autorun))
run_program(autorun.stored_data)
run_program(autorun.stored_data, user)
for(var/s in enabled_services)
var/datum/computer_file/program/service = s
if(service.program_type & PROGRAM_SERVICE) // Safety checks
service.service_activate()
service.service_state = PROGRAM_STATE_ACTIVE
if(user)
ui_interact(user)
@@ -201,9 +223,10 @@
ui_interact(user) // Re-open the UI on this computer. It should show the main screen now.
/obj/item/modular_computer/proc/run_program(prog)
/obj/item/modular_computer/proc/run_program(prog, mob/user)
var/datum/computer_file/program/P = null
var/mob/user = usr
if(!istype(user))
user = usr
if(hard_drive)
P = hard_drive.find_file_by_name(prog)
@@ -294,6 +317,58 @@
/obj/item/modular_computer/get_cell()
return battery_module ? battery_module.get_cell() : DEVICE_NO_CELL
/obj/item/modular_computer/proc/toggle_service(service, mob/user, var/datum/computer_file/program/S = null)
if(!S)
S = hard_drive?.find_file_by_name(service)
if(!istype(S)) // Program not found or it's not executable program.
to_chat(user, SPAN_WARNING("\The [src] displays, \"I/O ERROR - Unable to locate [service]\""))
return
if(S.service_state == PROGRAM_STATE_ACTIVE)
disable_service(null, user, S)
else
enable_service(null, user, S)
/obj/item/modular_computer/proc/enable_service(service, mob/user, var/datum/computer_file/program/S = null)
if(!S)
S = hard_drive?.find_file_by_name(service)
if(!istype(S)) // Program not found or it's not executable program.
to_chat(user, SPAN_WARNING("\The [src] displays, \"I/O ERROR - Unable to enable [service]\""))
return
S.computer = src
if(!S.is_supported_by_hardware(hardware_flag, 1, user))
return
if(S in enabled_services)
return
enabled_services += S
// Start service
S.service_activate()
S.service_state = PROGRAM_STATE_ACTIVE
/obj/item/modular_computer/proc/disable_service(service, mob/user, var/datum/computer_file/program/S = null)
if(!S)
S = hard_drive?.find_file_by_name(service)
if(!istype(S)) // Program not found or it's not executable program.
return
if(!(S in enabled_services))
return
enabled_services -= S
// Stop service
S.service_deactivate()
S.service_state = PROGRAM_STATE_KILLED
/obj/item/modular_computer/proc/output_message(var/message, var/message_range)
message_range += message_output_range
if(message_range == 0)
@@ -301,4 +376,4 @@
if(istype(user))
to_chat(user, message)
return
visible_message(message, range = message_range)
visible_message(message, range = message_range)

View File

@@ -51,6 +51,10 @@
VUEUI_SET_CHECK(data["programs"][P.filename]["desc"], P.filedesc, ., data)
VUEUI_SET_CHECK(data["programs"][P.filename]["autorun"], (istype(autorun) && (autorun.stored_data == P.filename)), ., data)
VUEUI_SET_CHECK(data["programs"][P.filename]["running"], (P in idle_threads), ., data)
VUEUI_SET_CHECK(data["programs"][P.filename]["type"], P.program_type, ., data)
VUEUI_SET_CHECK_IFNOTSET(data["programs"][P.filename]["service"], list(), ., data)
VUEUI_SET_CHECK(data["programs"][P.filename]["service"]["enabled"], (P in enabled_services), ., data)
VUEUI_SET_CHECK(data["programs"][P.filename]["service"]["online"], (P.service_state == PROGRAM_STATE_ACTIVE), ., data)
// Handles user's GUI input
/obj/item/modular_computer/Topic(href, href_list)
@@ -90,8 +94,8 @@
update_uis()
to_chat(user, SPAN_NOTICE("Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed."))
if(href_list["PC_runprogram"])
. = run_program(href_list["PC_runprogram"])
if(href_list["PC_runprogram"] )
. = run_program(href_list["PC_runprogram"], usr)
ui_interact(usr)
if(href_list["PC_setautorun"])
@@ -106,6 +110,10 @@
autorun.stored_data = null
else
autorun.stored_data = href_list["PC_setautorun"]
if( href_list["PC_toggleservice"] )
toggle_service(href_list["PC_toggleservice"], usr)
return 1
if(.)
update_uis()

View File

@@ -41,6 +41,7 @@
var/steel_sheet_cost = 5 // Amount of steel sheets refunded when disassembling an empty frame of this computer.
var/light_strength = 0 // Intensity of light this computer emits. Comparable to numbers light fixtures use.
var/list/idle_threads = list() // Idle programs on background. They still receive process calls but can't be interacted with.
var/list/enabled_services = list() // Enabled services that run in background and handle things pasively. Supported on all CPUs.
var/power_has_failed = FALSE
var/is_holographic = FALSE

View File

@@ -10,8 +10,13 @@
base_active_power_usage = 25
max_hardware_size = 3
max_damage = 50
w_class = ITEMSIZE_NORMAL
enrolled = 2
var/mob/living/silicon/computer_host // Thing that contains this computer. Used for silicon computers
/obj/item/modular_computer/silicon/ui_host()
. = computer_host
/obj/item/modular_computer/silicon/Initialize(mapload)
. = ..()
if(istype(loc, /mob/living/silicon))
@@ -30,10 +35,10 @@
if(istype(computer_host, /mob/living/silicon/robot))
var/mob/living/silicon/robot/R = computer_host
return R.cell_use_power(power_usage)
// If we are in AI or pAI we just don't botherwith power use.
// If we are in AI or pAI we just don't bother with power use.
return TRUE
else
// If we don't have host, then we let regular computer code handle power - like batteries and tesla coils
// If we don't have host, then we let regular computer code handle power - like batteries and tesla coils.
return ..()
/obj/item/modular_computer/silicon/Destroy()
@@ -42,3 +47,14 @@
/obj/item/modular_computer/silicon/Click(location, control, params)
return attack_self(usr)
/obj/item/modular_computer/silicon/install_default_hardware()
. = ..()
processor_unit = new /obj/item/computer_hardware/processor_unit(src)
hard_drive = new /obj/item/computer_hardware/hard_drive(src)
network_card = new /obj/item/computer_hardware/network_card/advanced(src)
/obj/item/modular_computer/silicon/install_default_programs()
hard_drive.store_file(new /datum/computer_file/program/filemanager())
hard_drive.store_file(new /datum/computer_file/program/ntnetdownload())
hard_drive.remove_file(hard_drive.find_file_by_name("clientmanager"))

View File

@@ -0,0 +1,28 @@
/obj/item/modular_computer/silicon/ai
hardware_flag = PROGRAM_SILICON_AI
/obj/item/modular_computer/silicon/ai/install_default_hardware()
. = ..()
network_card = new /obj/item/computer_hardware/network_card/wired(src)
/obj/item/modular_computer/silicon/ai/install_default_programs()
. = ..()
hard_drive.store_file(new /datum/computer_file/program/records())
hard_drive.store_file(new /datum/computer_file/program/rcon_console())
hard_drive.store_file(new /datum/computer_file/program/suit_sensors())
hard_drive.store_file(new /datum/computer_file/program/power_monitor())
/obj/item/modular_computer/silicon/robot
hardware_flag = PROGRAM_SILICON_ROBOT
/obj/item/modular_computer/silicon/pai
hardware_flag = PROGRAM_SILICON_PAI
/obj/item/modular_computer/silicon/ai/install_default_hardware()
. = ..()
hard_drive = new /obj/item/computer_hardware/hard_drive/small(src)
/obj/item/modular_computer/silicon/pai/install_default_programs()
. = ..()
hard_drive.store_file(new /datum/computer_file/program/pai_directives())
hard_drive.store_file(new /datum/computer_file/program/pai_radio())

View File

@@ -1,13 +0,0 @@
/obj/item/modular_computer/silicon/preset
enrolled = 2
/obj/item/modular_computer/silicon/preset/install_default_hardware()
. = ..()
processor_unit = new /obj/item/computer_hardware/processor_unit(src)
hard_drive = new /obj/item/computer_hardware/hard_drive(src)
network_card = new /obj/item/computer_hardware/network_card/advanced(src)
/obj/item/modular_computer/silicon/preset/install_default_programs()
hard_drive.store_file(new /datum/computer_file/program/filemanager())
hard_drive.store_file(new /datum/computer_file/program/ntnetdownload())
hard_drive.remove_file(hard_drive.find_file_by_name("clientmanager"))

View File

@@ -2,6 +2,7 @@
/datum/computer_file/program
filetype = "PRG"
filename = "UnknownProgram" // File name. FILE NAME MUST BE UNIQUE IF YOU WANT THE PROGRAM TO BE DOWNLOADABLE FROM NTNET!
var/program_type = PROGRAM_NORMAL // Program type, used to determine if program runs in background or not.
var/required_access_run // List of required accesses to run the program.
var/required_access_download // List of required accesses to download the program.
var/requires_access_to_run = PROGRAM_ACCESS_ONE // Whether the program checks for required_access when run. (1 = requires single access, 2 = requires single access from list, 3 = requires all access from list)
@@ -22,7 +23,8 @@
var/available_on_syndinet = FALSE // Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to 1 to enable.
var/computer_emagged = FALSE // Set to TRUE if computer that's running us was emagged. Computer updates this every Process() tick
var/ui_header // Example: "something.gif" - a header image that will be rendered in computer's UI when this program is running at background. Images are taken from /nano/images/status_icons. Be careful not to use too large images!
var/color = "#FFFFFF" // The color of light the computer should emit when this program is open.
var/color = "#FFFFFF" // The color of light the computer should emit when this program is open.
var/service_state = PROGRAM_STATE_KILLED // PROGRAM_STATE_KILLED or PROGRAM_STATE_ACTIVE - specifies whether this program's service is running.
/datum/computer_file/program/New(var/obj/item/modular_computer/comp)
..()
@@ -30,6 +32,8 @@
computer = comp
/datum/computer_file/program/Destroy()
computer.idle_threads -= src
computer.enabled_services -= src
computer = null
. = ..()
@@ -135,6 +139,10 @@
if(!check_type)
check_type = requires_access_to_download
if(istype(computer, /obj/item/modular_computer/silicon/pai))
check_type = requires_access_to_run
access_to_check = required_access_run
// No required_access, allow it.
if(!access_to_check || !requires_access_to_download)
return TRUE
@@ -213,7 +221,6 @@
// CONVENTIONS, READ THIS WHEN CREATING NEW PROGRAM AND OVERRIDING THIS PROC:
// Topic calls are automagically forwarded from NanoModule this program contains.
// Calls beginning with "PRG_" are reserved for programs handling.
// Calls beginning with "PC_" are reserved for computer handling (by whatever runs the program)
// ALWAYS INCLUDE PARENT CALL ..() OR DIE IN FIRE.
/datum/computer_file/program/Topic(href, href_list)
@@ -228,3 +235,12 @@
return NM.check_eye(user)
else
return -1
// Is called when program service is being activated
// Returns 1 if service startup was sucessfull
/datum/computer_file/program/proc/service_activate()
return 0
// Is called when program service is being deactivated
/datum/computer_file/program/proc/service_deactivate()
return

View File

@@ -1,6 +1,9 @@
/obj/item/modular_computer/initial_data()
return list("_PC" = get_header_data())
/datum/computer_file/program/initial_data()
return list("_PC" = get_header_data())
/obj/item/modular_computer/update_layout()
return TRUE

View File

@@ -21,8 +21,7 @@
return
computer.visible_message(SPAN_NOTICE("\The [computer]'s screen brightly flashes and emits a loud electrical buzzing."))
computer.enabled = FALSE
computer.update_icon()
computer.shutdown_computer(0)
spark(get_turf(src), 10, alldirs)
if(computer.hard_drive)

View File

@@ -72,7 +72,7 @@
requires_ntnet = TRUE
network_destination = "atmospheric control system"
requires_ntnet_feature = NTNET_SYSTEMCONTROL
usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP | PROGRAM_SILICON
usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP | PROGRAM_STATIONBOUND
size = 17
color = LIGHT_COLOR_CYAN
@@ -87,7 +87,7 @@
requires_ntnet = TRUE
network_destination = "RCON remote control system"
requires_ntnet_feature = NTNET_SYSTEMCONTROL
usage_flags = PROGRAM_CONSOLE | PROGRAM_SILICON
usage_flags = PROGRAM_CONSOLE | PROGRAM_STATIONBOUND
size = 19
color = LIGHT_COLOR_GREEN
@@ -103,6 +103,6 @@
requires_ntnet = TRUE
network_destination = "APC Coordinator"
requires_ntnet_feature = NTNET_SYSTEMCONTROL
usage_flags = PROGRAM_CONSOLE | PROGRAM_SILICON
usage_flags = PROGRAM_CONSOLE | PROGRAM_STATIONBOUND
size = 9
color = LIGHT_COLOR_GREEN

View File

@@ -0,0 +1,44 @@
/datum/computer_file/program/manifest
filename = "manifest"
filedesc = "Crew Manifest"
program_icon_state = "generic"
extended_desc = "This program is used for viewing the crew manifest."
size = 3
requires_ntnet = 1
available_on_ntnet = 1
/datum/computer_file/program/manifest/ui_interact(mob/user)
var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
if (!ui)
ui = new /datum/vueui/modularcomputer(user, src, "manifest", 450, 600, "Crew Manifest")
ui.open()
/datum/computer_file/program/manifest/vueui_transfer(oldobj)
SSvueui.transfer_uis(oldobj, src, "manifest", 450, 600, "Crew Manifest")
return TRUE
// Gaters data for ui
/datum/computer_file/program/manifest/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui)
. = ..()
data = . || data || list()
// Gather data for computer header
var/headerdata = get_header_data(data["_PC"])
if(headerdata)
data["_PC"] = headerdata
. = data
VUEUI_SET_CHECK(data["manifest"], SSrecords.get_manifest_list(), ., data)
/datum/computer_file/program/manifest/Topic(href, href_list)
. = ..()
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
host.radio.Topic(href, href_list)

View File

@@ -15,6 +15,7 @@
var/datum/ntnet_conversation/channel
var/operator_mode = FALSE // Channel operator mode
var/netadmin_mode = FALSE // Administrator mode (invisible to other users + bypasses passwords)
var/list/directmessagechannels = list()
color = LIGHT_COLOR_GREEN
/datum/computer_file/program/chatclient/New()
@@ -60,9 +61,12 @@
channel = C
if(href_list["PRG_leavechannel"])
. = TRUE
if(channel)
if(channel && !channel.direct)
channel.remove_client(src)
channel = null
if(href_list["PRG_backtomain"])
. = TRUE
channel = null
if(href_list["PRG_newchannel"])
. = TRUE
var/mob/living/user = usr
@@ -98,10 +102,16 @@
var/new_name = sanitize(input(user, "Enter new nickname or leave blank to cancel:"))
if(!new_name)
return TRUE
if(channel)
channel.add_status_message("[username] is now known as [new_name].")
var/comp_name = ckey(new_name)
for(var/cl in ntnet_global.chat_clients)
var/datum/computer_file/program/chatclient/C = cl
if(ckey(C.username) == comp_name || comp_name == "cancel")
alert(user, "This nickname is already taken.")
return TRUE
for(var/datum/ntnet_conversation/channel in ntnet_global.chat_channels)
if(src in channel.clients)
channel.add_status_message("[username] is now known as [new_name].")
username = new_name
if(href_list["PRG_savelog"])
. = TRUE
if(!channel)
@@ -156,6 +166,30 @@
channel.password = ""
else
channel.password = newpassword
if(href_list["PRG_directmessage"])
. = TRUE
var/clients = list()
var/names = list()
for(var/cl in ntnet_global.chat_clients)
var/datum/computer_file/program/chatclient/C = cl
clients[C.username] = C
names += C.username
names -= username // Remove ourselves
names += "== Cancel =="
var/picked = input(usr, "Select with whom you would like to start a conversation.") in names
if(picked == "== Cancel ==")
return
var/datum/computer_file/program/chatclient/otherClient = clients[picked]
if(picked)
if(directmessagechannels[otherClient])
channel = directmessagechannels[otherClient]
return
var/datum/ntnet_conversation/C = new /datum/ntnet_conversation("", TRUE)
C.begin_direct(src, otherClient)
channel = C
directmessagechannels[otherClient] = C
otherClient.directmessagechannels[src] = C
/datum/computer_file/program/chatclient/process_tick()
..()
@@ -176,11 +210,14 @@
ui_header = "ntnrc_idle.gif"
/datum/computer_file/program/chatclient/kill_program(var/forced = FALSE)
if(channel)
channel.remove_client(src)
channel = null
channel = null
ntnet_global.chat_clients -= src
..(forced)
/datum/computer_file/program/chatclient/run_program(var/mob/user)
ntnet_global.chat_clients += src
return ..(user)
/datum/nano_module/program/computer_chatclient
name = "NTNet Relay Chat Client"
@@ -198,7 +235,7 @@
data["adminmode"] = C.netadmin_mode
if(C.channel)
data["title"] = C.channel.title
data["title"] = C.channel.get_title(C)
var/list/messages[0]
for(var/M in C.channel.messages)
messages.Add(list(list(
@@ -208,19 +245,21 @@
var/list/clients[0]
for(var/datum/computer_file/program/chatclient/cl in C.channel.clients)
clients.Add(list(list(
"name" = cl.username
"name" = cl.username,
"active" = cl.program_state > PROGRAM_STATE_KILLED
)))
data["clients"] = clients
C.operator_mode = (C.channel.operator == C) ? 1 : 0
data["is_operator"] = C.operator_mode || C.netadmin_mode
data["is_direct"] = C.channel.direct
else // Channel selection screen
var/list/all_channels[0]
for(var/datum/ntnet_conversation/conv in ntnet_global.chat_channels)
if(conv && conv.title)
if(conv && conv.title && conv.can_see(program))
all_channels.Add(list(list(
"chan" = conv.title,
"id" = conv.id
"chan" = conv.get_title(C),
"id" = conv.id,
"con" = (program in conv.clients)
)))
data["all_channels"] = all_channels

View File

@@ -11,6 +11,7 @@
requires_ntnet = TRUE
requires_ntnet_feature = NTNET_SYSTEMCONTROL
requires_access_to_run = PROGRAM_ACCESS_LIST_ONE
usage_flags = PROGRAM_ALL_REGULAR | PROGRAM_STATIONBOUND
var/records_type = RECORD_GENERAL | RECORD_MEDICAL | RECORD_SECURITY | RECORD_VIRUS | RECORD_WARRANT | RECORD_LOCKED
var/edit_type = RECORD_GENERAL | RECORD_MEDICAL | RECORD_SECURITY | RECORD_VIRUS | RECORD_WARRANT | RECORD_LOCKED
@@ -77,6 +78,12 @@
program_icon_state = "employment_record"
color = LIGHT_COLOR_BLUE
/datum/computer_file/program/records/pai
available_on_ntnet = 1
extended_desc = "This program is used to view crew records."
usage_flags = PROGRAM_SILICON_PAI
edit_type = 0
/datum/computer_file/program/records/New()
. = ..()
listener = new(src)
@@ -339,4 +346,4 @@
var/datum/computer_file/program/records/t = target
if(istype(t) && !t.isEditing)
if(t.active == r || t.active_virus == r)
SSvueui.check_uis_for_change(t)
SSvueui.check_uis_for_change(t)

View File

@@ -8,7 +8,7 @@
requires_ntnet = TRUE
network_destination = "crew lifesigns monitoring system"
size = 11
usage_flags = PROGRAM_ALL_REGULAR
usage_flags = PROGRAM_ALL_REGULAR | PROGRAM_STATIONBOUND
color = LIGHT_COLOR_CYAN
/datum/computer_file/program/suit_sensors/ui_interact(mob/user)

View File

@@ -0,0 +1,59 @@
/datum/computer_file/program/pai_atmos
filename = "pai_atmos"
filedesc = "Atmosphere Sensor"
program_icon_state = "generic"
extended_desc = "This program is for viewing local atmospheric data."
size = 0
usage_flags = PROGRAM_SILICON_PAI
/datum/computer_file/program/pai_atmos/ui_interact(mob/user)
var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
if (!ui)
ui = new /datum/vueui/modularcomputer(user, src, "mcomputer-pai-atmosphere", 400, 200, "pAI Atmosphere Sensor")
ui.open()
/datum/computer_file/program/pai_atmos/vueui_transfer(oldobj)
SSvueui.transfer_uis(oldobj, src, "mcomputer-pai-atmosphere", 400, 200, "pAI Atmosphere Sensor")
return TRUE
// Gaters data for ui
/datum/computer_file/program/pai_atmos/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui)
. = ..()
data = . || data || list()
// Gather data for computer header
var/headerdata = get_header_data(data["_PC"])
if(headerdata)
data["_PC"] = headerdata
. = data
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
var/turf/T = get_turf_or_move(host.loc)
if(T)
var/datum/gas_mixture/env = T.return_air()
VUEUI_SET_CHECK(data["read"], TRUE, ., data)
VUEUI_SET_CHECK(data["press"], env.return_pressure(), ., data)
VUEUI_SET_CHECK(data["temp"], env.temperature, ., data)
VUEUI_SET_CHECK(data["tempC"], env.temperature-T0C, ., data)
VUEUI_SET_CHECK_IFNOTSET(data["gas"], list("_" = "_"), ., data)
for(var/g in gas_data.gases)
if(!(g in env.gas))
VUEUI_SET_CHECK(data["gas"][g], null, ., data)
else
VUEUI_SET_CHECK(data["gas"][g], env.gas[g], ., data)
else
VUEUI_SET_CHECK(data["read"], FALSE, ., data)
VUEUI_SET_CHECK(data["press"], 0, ., data)
VUEUI_SET_CHECK(data["temp"], T0C, ., data)
VUEUI_SET_CHECK(data["tempC"], 0, ., data)
VUEUI_SET_CHECK_IFNOTSET(data["gas"], list("_" = "_"), ., data)
for(var/g in gas_data.gases)
VUEUI_SET_CHECK(data["gas"][g], null, ., data)

View File

@@ -0,0 +1,79 @@
/datum/computer_file/program/pai_directives
filename = "pai_directives"
filedesc = "pAI Directives"
program_icon_state = "generic"
extended_desc = "This program is for viewing currently assigned directives."
size = 0
usage_flags = PROGRAM_SILICON_PAI
/datum/computer_file/program/pai_directives/ui_interact(mob/user)
var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
if (!ui)
ui = new /datum/vueui/modularcomputer(user, src, "mcomputer-pai-directives", 450, 500, "pAI directives")
ui.open()
/datum/computer_file/program/pai_directives/vueui_transfer(oldobj)
SSvueui.transfer_uis(oldobj, src, "mcomputer-pai-directives", 450, 500, "pAI directives")
return TRUE
// Gaters data for ui
/datum/computer_file/program/pai_directives/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui)
. = ..()
data = . || data || list()
// Gather data for computer header
var/headerdata = get_header_data(data["_PC"])
if(headerdata)
data["_PC"] = headerdata
. = data
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
VUEUI_SET_CHECK(data["master"], host.master, ., data)
VUEUI_SET_CHECK(data["dna"], host.master_dna, ., data)
VUEUI_SET_CHECK(data["prime"], host.pai_law0, ., data)
VUEUI_SET_CHECK(data["supplemental"], host.pai_laws, ., data)
/datum/computer_file/program/pai_directives/Topic(href, href_list)
. = ..()
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
if(href_list["getdna"])
var/mob/living/M = host.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(src, SPAN_WARNING("You are not being carried by anyone!"))
return 0
M = M.loc
count++
// Check the carrier
var/answer = input(M, "[host] is requesting a DNA sample from you. Will you allow it to confirm your identity?", "[host] Check DNA", "No") in list("Yes", "No")
if(answer == "Yes")
var/turf/T = get_turf_or_move(host.loc)
for (var/mob/v in viewers(T))
v.show_message(SPAN_NOTICE("[M] presses \his thumb against [host]."), 3, SPAN_NOTICE("[host] makes a sharp clicking sound as it extracts DNA material from [M]."), 2)
var/datum/dna/dna = M.dna
to_chat(host, "<font color = red><h3>[M]'s UE string : [dna.unique_enzymes]</h3></font>")
if(dna.unique_enzymes == host.master_dna)
to_chat(host, SPAN_NOTICE("<b>Provided DNA is a match to stored Master DNA.</b>"))
else
to_chat(host, SPAN_WARNING("<b>Provided DNA does not match stored Master DNA.</b>"))
else
to_chat(host, SPAN_WARNING("[M] does not seem like \he [gender_datums[M.gender].is] going to provide a DNA sample willingly."))
return 1

View File

@@ -0,0 +1,106 @@
/datum/computer_file/program/pai_doorjack
filename = "doorjack"
filedesc = "Door Jack"
program_icon_state = "generic"
extended_desc = "This program is used to access standard-issue pAI door jack systems."
size = 12
available_on_ntnet = 1
usage_flags = PROGRAM_SILICON_PAI
/datum/computer_file/program/pai_doorjack/ui_interact(mob/user)
var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
if (!ui)
ui = new /datum/vueui/modularcomputer(user, src, "mcomputer-pai-doorjack", 400, 150, "Door Jack")
ui.auto_update_content = TRUE
ui.open()
/datum/computer_file/program/pai_doorjack/vueui_transfer(oldobj)
var/uis = SSvueui.transfer_uis(oldobj, src, "mcomputer-pai-doorjack", 400, 150, "Door Jack")
for(var/i in uis)
var/datum/vueui/ui = i
ui.auto_update_content = TRUE
return TRUE
/datum/computer_file/program/pai_doorjack/vueui_on_transfer(datum/vueui/ui)
. = ..()
ui.auto_update_content = FALSE
// Gaters data for ui
/datum/computer_file/program/pai_doorjack/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui)
. = ..()
data = . || data || list()
// Gather data for computer header
var/headerdata = get_header_data(data["_PC"])
if(headerdata)
data["_PC"] = headerdata
. = data
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
VUEUI_SET_CHECK(data["extended"], !!host.cable, ., data)
VUEUI_SET_CHECK(data["connected"], !!host.cable?.machine , ., data)
VUEUI_SET_CHECK(data["ishacking"], !!host.hackdoor, ., data)
VUEUI_SET_CHECK(data["progress"], host.hackprogress, ., data)
VUEUI_SET_CHECK(data["aborted"], host.hack_aborted, ., data)
/datum/computer_file/program/pai_doorjack/Topic(href, href_list)
. = ..()
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
if(href_list["hack"])
if(host.cable && host.cable.machine)
host.hackdoor = host.cable.machine
host.hackloop()
return 1
if(href_list["cancel"])
host.hackdoor = null
return 1
if(href_list["extend"])
var/turf/T = get_turf_or_move(host.loc)
host.hack_aborted = 0
host.cable = new /obj/item/pai_cable(T)
T.visible_message(SPAN_WARNING("A port on [host] opens to reveal [host.cable], which promptly falls to the floor."),
SPAN_WARNING("You hear the soft click of something light and hard falling to the ground."))
return 1
/mob/living/silicon/pai/proc/hackloop()
var/turf/T = get_turf_or_move(src.loc)
for(var/mob/living/silicon/ai/AI in player_list)
if(T.loc)
to_chat(AI, "<font color = red><b>Network Alert: Brute-force encryption crack in progress in [T.loc].</b></font>")
else
to_chat(AI, "<font color = red><b>Network Alert: Brute-force encryption crack in progress. Unable to pinpoint location.</b></font>")
var/obj/machinery/door/airlock/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.unlock() //unbolts door as long as bolt wires aren't cut
D.open()
cable.machine = null
return
sleep(10) // Update every second

View File

@@ -0,0 +1,57 @@
/datum/computer_file/program/pai_sechud
filename = "sechud"
filedesc = "pAI Security HUD"
extended_desc = "This service enables the integrated security HUD."
size = 10
program_type = PROGRAM_SERVICE
available_on_ntnet = 1
usage_flags = PROGRAM_SILICON_PAI
/datum/computer_file/program/pai_sechud/service_activate()
. = ..()
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
host.secHUD = TRUE
/datum/computer_file/program/pai_sechud/service_deactivate()
. = ..()
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
host.secHUD = FALSE
/datum/computer_file/program/pai_medhud
filename = "medhud"
filedesc = "pAI Medical HUD"
extended_desc = "This service enables the integrated medical HUD."
size = 10
program_type = PROGRAM_SERVICE
available_on_ntnet = 1
usage_flags = PROGRAM_SILICON_PAI
/datum/computer_file/program/pai_medhud/service_activate()
. = ..()
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
host.medHUD = TRUE
/datum/computer_file/program/pai_medhud/service_deactivate()
. = ..()
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
host.medHUD = FALSE

View File

@@ -0,0 +1,56 @@
/datum/computer_file/program/pai_radio
filename = "pai_radio"
filedesc = "Radio Configuration"
program_icon_state = "generic"
extended_desc = "This program is used to configure the integrated pAI radio."
size = 0
usage_flags = PROGRAM_SILICON_PAI
/datum/computer_file/program/pai_radio/ui_interact(mob/user)
var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
if (!ui)
ui = new /datum/vueui/modularcomputer(user, src, "mcomputer-pai-radio", 400, 150, "pAI Radio Configuration")
ui.open()
/datum/computer_file/program/pai_radio/vueui_transfer(oldobj)
SSvueui.transfer_uis(oldobj, src, "mcomputer-pai-radio", 400, 150, "pAI Radio Configuration")
return TRUE
// Gaters data for ui
/datum/computer_file/program/pai_radio/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui)
. = ..()
data = . || data || list()
// Gather data for computer header
var/headerdata = get_header_data(data["_PC"])
if(headerdata)
data["_PC"] = headerdata
. = data
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
VUEUI_SET_CHECK(data["listening"], host.radio.broadcasting, ., data)
VUEUI_SET_CHECK(data["frequency"], format_frequency(host.radio.frequency), ., data)
LAZYINITLIST(data["channels"])
for(var/ch_name in host.radio.channels)
var/ch_stat = host.radio.channels[ch_name]
VUEUI_SET_CHECK(data["channels"][ch_name], !!(ch_stat & host.radio.FREQ_LISTENING), ., data)
/datum/computer_file/program/pai_radio/Topic(href, href_list)
. = ..()
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
host.radio.Topic(href, href_list)
SSvueui.check_uis_for_change(src)

View File

@@ -0,0 +1,69 @@
/datum/computer_file/program/pai_signaler
filename = "signaler"
filedesc = "Remote Signaller"
program_icon_state = "generic"
extended_desc = "This program can be used to send wide-range signals of various frequencies."
size = 3
available_on_ntnet = 1
usage_flags = PROGRAM_SILICON_PAI
/datum/computer_file/program/pai_signaler/ui_interact(mob/user)
var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
if (!ui)
ui = new /datum/vueui/modularcomputer(user, src, "mcomputer-pai-signaler", 400, 150, "pAI Signaller")
ui.open()
/datum/computer_file/program/pai_signaler/vueui_transfer(oldobj)
SSvueui.transfer_uis(oldobj, src, "mcomputer-pai-signaler", 400, 150, "pAI Signaller")
return TRUE
// Gaters data for ui
/datum/computer_file/program/pai_signaler/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui)
. = ..()
data = . || data || list()
// Gather data for computer header
var/headerdata = get_header_data(data["_PC"])
if(headerdata)
data["_PC"] = headerdata
. = data
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
VUEUI_SET_CHECK(data["code"], host.sradio.code, ., data)
VUEUI_SET_CHECK(data["frequency"], format_frequency(host.sradio.frequency), ., data)
/datum/computer_file/program/pai_signaler/Topic(href, href_list)
. = ..()
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
if(href_list["send"])
host.sradio.send_signal("ACTIVATE")
for(var/mob/O in hearers(1, host.loc))
O.show_message(text("\icon[] *beep* *beep*", host), 3, "*beep* *beep*", 2)
return 1
else if(href_list["freq"])
var/new_frequency = (host.sradio.frequency + href_list["freq"])
if(new_frequency < PUBLIC_LOW_FREQ || new_frequency > PUBLIC_HIGH_FREQ)
new_frequency = sanitize_frequency(new_frequency)
host.sradio.set_frequency(new_frequency)
return 1
else if(href_list["code"])
host.sradio.code += href_list["code"]
host.sradio.code = round(host.sradio.code)
host.sradio.code = min(100, host.sradio.code)
host.sradio.code = max(1, host.sradio.code)
return 1

View File

@@ -0,0 +1,40 @@
/datum/computer_file/program/pai_translator
filename = "translator"
filedesc = "pAI Universal Translator"
extended_desc = "This service enables the integrated universal translation systems."
size = 12
program_type = PROGRAM_SERVICE
available_on_ntnet = 1
usage_flags = PROGRAM_SILICON_PAI
/datum/computer_file/program/pai_translator/service_activate()
. = ..()
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
if(!host.translator_on)
host.add_language(LANGUAGE_UNATHI)
host.add_language(LANGUAGE_SIIK_MAAS)
host.add_language(LANGUAGE_SKRELLIAN)
host.add_language(LANGUAGE_ROOTSONG)
host.translator_on = TRUE
/datum/computer_file/program/pai_translator/service_deactivate()
. = ..()
if(!istype(computer, /obj/item/modular_computer/silicon))
return
var/obj/item/modular_computer/silicon/true_computer = computer
if(!istype(true_computer.computer_host, /mob/living/silicon/pai))
return
var/mob/living/silicon/pai/host = true_computer.computer_host
if(host.translator_on)
host.remove_language(LANGUAGE_UNATHI)
host.remove_language(LANGUAGE_SIIK_MAAS)
host.remove_language(LANGUAGE_SKRELLIAN)
host.remove_language(LANGUAGE_ROOTSONG)
host.translator_on = FALSE

View File

@@ -7,7 +7,7 @@
requires_ntnet = TRUE
required_access_run = access_network
required_access_download = access_heads
usage_flags = PROGRAM_CONSOLE
usage_flags = PROGRAM_CONSOLE | PROGRAM_SILICON_AI
color = LIGHT_COLOR_GREEN
available_on_ntnet = TRUE

View File

@@ -10,6 +10,7 @@
required_access_download = access_hos
required_access_run = access_security
nanomodule_path = /datum/nano_module/program/digitalwarrant
usage_flags = PROGRAM_ALL_REGULAR | PROGRAM_STATIONBOUND
/datum/nano_module/program/digitalwarrant
name = "Warrant Assistant"

View File

@@ -9,231 +9,224 @@
size = 4
requires_ntnet = TRUE
requires_ntnet_feature = NTNET_SOFTWAREDOWNLOAD
available_on_ntnet = FALSE
nanomodule_path = /datum/nano_module/program/computer_ntnetdownload
available_on_ntnet = 0
ui_header = "downloader_finished.gif"
var/datum/computer_file/program/downloaded_file
var/hacked_download = FALSE
var/download_completion = 0 //GQ of downloaded data.
var/download_netspeed = 0
var/download_last_update = 0 // For tracking download rates and completion.
var/downloaderror = ""
var/downstream_variance = 0.1
var/list/download_queue = list()
var/list/download_files = list()
var/queue_size = 0
var/active_download = null
var/last_update = 0
var/speed = 0
/datum/computer_file/program/ntnetdownload/proc/begin_file_download(var/filename, var/user)
if(downloaded_file)
return FALSE
var/datum/computer_file/program/PRG = ntnet_global.find_ntnet_file_by_name(filename)
/datum/computer_file/program/ntnetdownload/ui_interact(mob/user, var/datum/topic_state/state = default_state)
var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
if (!ui)
ui = new /datum/vueui/modularcomputer(user, src, "mcomputer-system-downloader", 575, 700, "NTNet Download Program", state = state)
ui.open()
if(!PRG || !istype(PRG))
return FALSE
/datum/computer_file/program/ntnetdownload/vueui_transfer(oldobj)
SSvueui.transfer_uis(oldobj, src, "mcomputer-system-downloader", 575, 700, "NTNet Download Program")
return TRUE
// Gaters data for ui
/datum/computer_file/program/ntnetdownload/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui)
. = ..()
data = . || data || list("queue" = download_queue)
if(!istype(computer))
return
// Gather data for computer header
var/headerdata = get_header_data(data["_PC"])
if(headerdata)
data["_PC"] = headerdata
. = data
// Let's send all installed programs
LAZYINITLIST(data["installed"])
for(var/datum/computer_file/program/I in hard_drive.stored_files)
LAZYINITLIST(data["installed"][I.filename])
VUEUI_SET_CHECK(data["installed"][I.filename]["name"], I.filedesc, ., data)
VUEUI_SET_CHECK(data["installed"][I.filename]["size"], I.size, ., data)
// Now lets send all avaivable programs with there status.
// Statuses (rest): 0 - ALL OK, 1 - can't download due to access, 2 - unsuported hardware, 3 - sindies only
LAZYINITLIST(data["avaivable"])
for(var/datum/computer_file/program/P in ntnet_global.available_software)
LAZYINITLIST(data["avaivable"][P.filename])
VUEUI_SET_CHECK(data["avaivable"][P.filename]["name"], P.filedesc, ., data)
VUEUI_SET_CHECK(data["avaivable"][P.filename]["desc"], P.extended_desc, ., data)
VUEUI_SET_CHECK(data["avaivable"][P.filename]["size"], P.size, ., data)
if(computer_emagged)
if(!P.is_supported_by_hardware(computer.hardware_flag))
VUEUI_SET_CHECK(data["avaivable"][P.filename]["rest"], 2, ., data)
else
VUEUI_SET_CHECK(data["avaivable"][P.filename]["rest"], 0, ., data)
else
if(!P.available_on_ntnet)
VUEUI_SET_CHECK(data["avaivable"][P.filename]["rest"], 3, ., data)
else if(!P.can_download(user) && P.requires_access_to_download)
VUEUI_SET_CHECK(data["avaivable"][P.filename]["rest"], 1, ., data)
else if(!P.is_supported_by_hardware(computer.hardware_flag))
VUEUI_SET_CHECK(data["avaivable"][P.filename]["rest"], 2, ., data)
else
VUEUI_SET_CHECK(data["avaivable"][P.filename]["rest"], 0, ., data)
VUEUI_SET_CHECK(data["disk_size"], hard_drive.max_capacity, ., data)
VUEUI_SET_CHECK(data["disk_used"], hard_drive.used_capacity, ., data)
VUEUI_SET_CHECK(data["queue_size"], queue_size, ., data)
VUEUI_SET_CHECK(data["speed"], speed, ., data)
for(var/name in download_queue)
VUEUI_SET_CHECK(data["queue"][name], download_queue[name], ., data)
/datum/computer_file/program/ntnetdownload/Topic(href, href_list)
if(..())
return 1
var/datum/vueui/ui = href_list["vueui"]
if(!istype(ui))
return
if(href_list["download"])
var/datum/computer_file/program/PRG = ntnet_global.find_ntnet_file_by_name(href_list["download"])
if(!istype(PRG))
return 1
return add_to_queue(PRG, ui.user)
if(href_list["cancel"])
return cancel_from_queue(href_list["cancel"])
/datum/computer_file/program/ntnetdownload/proc/add_to_queue(var/datum/computer_file/program/PRG, var/mob/user)
// Attempting to download antag only program, but without having emagged computer. No.
if(PRG.available_on_syndinet && !computer_emagged)
return FALSE
if(!hard_drive.can_store_file(queue_size + PRG.size))
to_chat(user, SPAN_WARNING("You can't download this program as queued items exceed hard drive capacity."))
return TRUE
if(!computer?.hard_drive?.try_store_file(PRG))
return FALSE
if(computer.enrolled == TRUE && !computer_emagged)
if(!computer_emagged && !PRG.can_download(user) && PRG.requires_access_to_download)
return TRUE
if(!PRG.is_supported_by_hardware(computer.hardware_flag))
return FALSE
ui_header = "downloader_running.gif"
if(PRG in ntnet_global.available_station_software)
if(PRG.available_on_ntnet)
generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from NTNet Software Repository.")
hacked_download = FALSE
else if(PRG in ntnet_global.available_antag_software)
else if (PRG.available_on_syndinet)
generate_network_log("Began downloading file **ENCRYPTED**.[PRG.filetype] from unspecified server.")
hacked_download = TRUE
else
generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from unspecified server.")
hacked_download = FALSE
downloaded_file = PRG.clone()
if(user)
spawn()
ui_interact(user)
/datum/computer_file/program/ntnetdownload/proc/abort_file_download()
if(!downloaded_file)
download_files[PRG.filename] = PRG.clone()
queue_size += PRG.size
download_queue[PRG.filename] = 0
for(var/i in SSvueui.get_open_uis(src))
var/datum/vueui/ui = i
ui.data["queue"][PRG.filename] = 0
ui.push_change()
return TRUE
/datum/computer_file/program/ntnetdownload/proc/cancel_from_queue(var/name)
if(!download_files[name])
return
generate_network_log("Aborted download of file [hacked_download ? "**ENCRYPTED**" : downloaded_file.filename].[downloaded_file.filetype].")
downloaded_file = null
download_completion = 0
download_last_update = 0
ui_header = "downloader_finished.gif"
var/datum/computer_file/program/PRG = download_files[name]
var/hacked_download = PRG.available_on_syndinet && !PRG.available_on_ntnet
generate_network_log("Aborted download of file [hacked_download ? "**ENCRYPTED**" : PRG.filename].[PRG.filetype].")
download_queue -= name
download_files -= name
queue_size -= PRG.size
for(var/i in SSvueui.get_open_uis(src))
var/datum/vueui/ui = i
ui.data["queue"] -= name
ui.push_change()
/datum/computer_file/program/ntnetdownload/proc/complete_file_download()
if(!downloaded_file)
/datum/computer_file/program/ntnetdownload/proc/finish_from_queue(var/name)
if(!download_files[name])
return
generate_network_log("Completed download of file [hacked_download ? "**ENCRYPTED**" : downloaded_file.filename].[downloaded_file.filetype].")
if(!computer?.hard_drive?.store_file(downloaded_file))
// The download failed
downloaderror = "I/O ERROR - Unable to save file. Check whether you have enough free space on your hard drive and whether your hard drive is properly connected. If the issue persists contact your system administrator for assistance."
else
playsound(get_turf(computer), 'sound/machines/ping.ogg', 40, 0)
computer.output_message("\icon[computer] <b>[capitalize_first_letters(computer)]</b> pings, \"Software download completed successfully!\"", 1)
downloaded_file = null
download_completion = 0
download_last_update = 0
ui_header = "downloader_finished.gif"
var/datum/computer_file/program/PRG = download_files[name]
var/hacked_download = PRG.available_on_syndinet && !PRG.available_on_ntnet
generate_network_log("Completed download of file [hacked_download ? "**ENCRYPTED**" : PRG.filename].[PRG.filetype].")
if(!computer?.hard_drive?.store_file(PRG))
download_queue[name] = -1
for(var/i in SSvueui.get_open_uis(src))
var/datum/vueui/ui = i
ui.data["queue"] = -1
ui.push_change()
return
download_queue -= name
download_files -= name
queue_size -= PRG.size
for(var/i in SSvueui.get_open_uis(src))
var/datum/vueui/ui = i
ui.data["queue"] -= name
ui.push_change()
/datum/computer_file/program/ntnetdownload/process_tick()
if(!downloaded_file)
if(!queue_size)
ui_header = "downloader_finished.gif"
return
if(download_completion >= downloaded_file.size)
complete_file_download()
return
// Download speed according to connectivity state. NTNet server is assumed to be on unlimited speed so we're limited by our local connectivity
download_netspeed = 0
// Speed defines are found in misc.dm
switch(ntnet_status)
if(1)
download_netspeed = NTNETSPEED_LOWSIGNAL
downstream_variance = 0.3
if(2)
download_netspeed = NTNETSPEED_HIGHSIGNAL
downstream_variance = 0.2
if(3)
download_netspeed = NTNETSPEED_ETHERNET
ui_header = "downloader_running.gif"
// We recovered connection or started a new download.
// So we don't have any bytes yet. We'll get them next time!
if (!download_last_update)
download_last_update = world.time
return
var/delta = ((rand() - 0.5) * 2) * downstream_variance * download_netspeed
//Download speed varies +/- 10% each proc. Adds a more realistic feel
download_netspeed += delta
download_netspeed = round(download_netspeed, 0.002)//3 decimal places
var/delta_seconds = (world.time - download_last_update) / 10
download_completion = min(download_completion + delta_seconds * download_netspeed, downloaded_file.size)
// No connection, so cancel the download.
// This is done at the end because of logic reasons.
// Trust me, it's fine. - Skull132
if (!download_netspeed)
download_last_update = 0
else
download_last_update = world.time
/datum/computer_file/program/ntnetdownload/Topic(href, href_list)
if(..())
return TRUE
if(href_list["PRG_downloadfile"])
if(!downloaded_file)
begin_file_download(href_list["PRG_downloadfile"], usr)
return TRUE
if(href_list["PRG_reseterror"])
if(downloaderror)
download_completion = 0
download_netspeed = 0
downloaded_file = null
downloaderror = ""
return TRUE
return FALSE
/datum/nano_module/program/computer_ntnetdownload
name = "Network Downloader"
var/obj/item/modular_computer/my_computer
/datum/nano_module/program/computer_ntnetdownload/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
if(program)
my_computer = program.computer
if(!istype(my_computer))
return
var/list/data = list()
var/datum/computer_file/program/ntnetdownload/prog = program
// For now limited to execution by the downloader program
if(!prog || !istype(prog))
return
if(program)
data = list("_PC" = program.get_header_data())
// This IF cuts on data transferred to client, so i guess it's worth it.
if(prog.downloaderror) // Download errored. Wait until user resets the program.
data["error"] = prog.downloaderror
else if(prog.downloaded_file) // Download running. Wait please..
if (ui)
ui.set_auto_update(TRUE)
data["downloadname"] = prog.downloaded_file.filename
data["downloaddesc"] = prog.downloaded_file.filedesc
data["downloadsize"] = prog.downloaded_file.size
data["downloadspeed"] = prog.download_netspeed //Even if it does update every 2 seconds, this is bad coding on everyone's count. :ree:
data["downloadcompletion"] = round(prog.download_completion, 0.01)
else // No download running, pick file.
if (ui)
ui.set_auto_update(FALSE)//No need for auto updating on the software menu
data["disk_size"] = my_computer.hard_drive.max_capacity
data["disk_used"] = my_computer.hard_drive.used_capacity
if(my_computer.enrolled == 2) //To lock installation of software on work computers until the IT Department is properly implemented - Then check for access on enrolled computers
data += get_programlist(user)
else
data["downloadable_programs"] = list()
data["locked"] = TRUE
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "ntnet_downloader.tmpl", "NTNet Download Program", 575, 700, state = state)
ui.auto_update_layout = TRUE
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(TRUE)
/datum/nano_module/program/computer_ntnetdownload/proc/get_programlist(var/mob/user)
var/list/all_entries[0]
var/datum/computer_file/program/ntnetdownload/prog = program
var/list/data = list()
data["hackedavailable"] = FALSE
for(var/datum/computer_file/program/P in ntnet_global.available_station_software)
var/installed = FALSE
for(var/datum/computer_file/program/Q in program.hard_drive.stored_files)
if(istype(P, Q.type))
installed = TRUE
if(download_queue[active_download] == null)
for(var/name in download_queue)
if(download_queue[name] >= 0)
active_download = name
break
if(download_queue[active_download] == null)
return
if(!installed)
// Only those programs our user can run will show in the list
if(!P.can_download(user) && P.requires_access_to_download)
continue
var/datum/computer_file/active_download_file = download_files[active_download]
if(download_queue[active_download] >= 0)
if (!last_update)
last_update = world.time
return
speed = 0
var/variance = 0.1
switch(ntnet_status)
if(1)
speed = NTNETSPEED_LOWSIGNAL
variance = 0.3
if(2)
speed = NTNETSPEED_HIGHSIGNAL
variance = 0.2
if(3)
speed = NTNETSPEED_ETHERNET
if(!P.is_supported_by_hardware(program.computer.hardware_flag))
continue
var/delta = ((rand() - 0.5) * 2) * variance * speed
//Download speed varies +/- 10% each proc. Adds a more realistic feels
speed += delta
speed = round(speed, 0.002)//3 decimal places
all_entries.Add(list(list(
"filename" = P.filename,
"filedesc" = P.filedesc,
"fileinfo" = P.extended_desc,
"size" = P.size
)))
var/delta_seconds = (world.time - last_update) / 10
if(prog.computer_emagged) // If we are running on emagged computer we have access to some "bonus" software
var/list/hacked_programs[0]
for(var/datum/computer_file/program/P in ntnet_global.available_antag_software)
var/installed = FALSE
for(var/datum/computer_file/program/Q in program.hard_drive.stored_files)
if(istype(P, Q.type))
installed = TRUE
break
download_queue[active_download] = min(download_queue[active_download] + delta_seconds * speed, active_download_file.size)
if(!installed)
data["hackedavailable"] = TRUE
hacked_programs.Add(list(list(
"filename" = P.filename,
"filedesc" = P.filedesc,
"fileinfo" = P.extended_desc,
"size" = P.size
)))
data["hacked_programs"] = hacked_programs
// No connection, so cancel the download.
// This is done at the end because of logic reasons.
// Trust me, it's fine. - Skull132
if (!speed)
last_update = 0
else
last_update = world.time
data["downloadable_programs"] = all_entries
return data
if(download_queue[active_download] >= active_download_file.size)
finish_from_queue(active_download)
active_download = null
playsound(get_turf(computer), 'sound/machines/ping.ogg', 40, 0)
computer.output_message("\icon[computer] <b>[capitalize_first_letters(computer)]</b> pings, \"Software download completed successfully!\"", 1)
SSvueui.check_uis_for_change(src)

View File

@@ -6,11 +6,11 @@
/datum/nano_module/New(var/datum/host, var/topic_manager)
..()
src.host = host.ui_host()
src.host = host
src.topic_manager = topic_manager
/datum/nano_module/ui_host()
return host ? host : src
return host ? host.ui_host() : src
/datum/nano_module/proc/can_still_topic(var/datum/topic_state/state = default_state)
return CanUseTopic(usr, state) == STATUS_INTERACTIVE

View File

@@ -36,4 +36,20 @@
/datum/proc/vueui_transfer(var/srcObject)
return FALSE
/**
* Callback to object when UI is being transfered away from it
*
* @param ui - that is being transfered
*/
/datum/proc/vueui_on_transfer(var/datum/vueui/ui)
return
/**
* Callback to object when UI is closed
*
* @param ui - that is being closed
*/
/datum/proc/vueui_on_close(var/datum/vueui/ui)
return
/mob/var/list/open_vueui_uis

View File

@@ -46,11 +46,11 @@ main ui datum.
*
* @return nothing
*/
/datum/vueui/New(var/nuser, var/nobject, var/nactiveui = 0, var/nwidth = 0, var/nheight = 0, var/ntitle, var/list/ndata, var/datum/topic_state/nstate = default_state)
/datum/vueui/New(var/nuser, var/nobject, var/nactiveui = 0, var/nwidth = 0, var/nheight = 0, var/ntitle, var/list/ndata, var/datum/topic_state/state = default_state)
user = nuser
object = nobject
data = ndata
state = nstate
src.state = state
LAZYINITLIST(assets)
if (nactiveui)
@@ -100,6 +100,7 @@ main ui datum.
* @return nothing
*/
/datum/vueui/proc/close()
object.vueui_on_close(src)
SSvueui.ui_closed(src)
user << browse(null, "window=[windowid]")
status = null