Computer tweaks (#1285)

Pulling in some computer changes from bay.
This commit is contained in:
Werner
2016-12-25 14:19:50 +02:00
committed by skull132
parent 93143a8e45
commit 5df9509435
94 changed files with 2682 additions and 989 deletions
+12 -1
View File
@@ -7,6 +7,7 @@ var/global/datum/ntnet/ntnet_global = new()
var/list/logs = list()
var/list/available_station_software = list()
var/list/available_antag_software = list()
var/list/available_news = list()
var/list/chat_channels = list()
var/list/fileservers = list()
// Amount of logs the system tries to keep in memory. Keep below 999 to prevent byond from acting weirdly.
@@ -32,6 +33,7 @@ var/global/datum/ntnet/ntnet_global = new()
relays.Add(R)
R.NTNet = src
build_software_lists()
build_news_list()
add_log("NTNet logging system activated.")
// Simplified logging: Adds a log. log_string is mandatory parameter, source is optional.
@@ -61,7 +63,7 @@ var/global/datum/ntnet/ntnet_global = new()
// Check all relays. If we have at least one working relay, network is up.
for(var/obj/machinery/ntnet_relay/R in relays)
if(R.is_operational())
if(R.operable())
operating = 1
break
@@ -93,6 +95,15 @@ var/global/datum/ntnet/ntnet_global = new()
if(prog.available_on_syndinet)
available_antag_software.Add(prog)
// Builds lists that contain downloadable software.
/datum/ntnet/proc/build_news_list()
available_news = list()
for(var/F in typesof(/datum/computer_file/data/news_article/))
var/datum/computer_file/data/news_article/news = new F(1)
if(news.stored_data)
available_news.Add(news)
// Attempts to find a downloadable file according to filename var
/datum/ntnet/proc/find_ntnet_file_by_name(var/filename)
for(var/datum/computer_file/program/P in available_station_software)
@@ -20,8 +20,8 @@
// TODO: Implement more logic here. For now it's only a placeholder.
/obj/machinery/ntnet_relay/proc/is_operational()
if(stat & (BROKEN | NOPOWER | EMPED))
/obj/machinery/ntnet_relay/operable()
if(!..(EMPED))
return 0
if(dos_failure)
return 0
@@ -30,13 +30,13 @@
return 1
/obj/machinery/ntnet_relay/update_icon()
if(is_operational())
if(operable())
icon_state = "bus"
else
icon_state = "bus_off"
/obj/machinery/ntnet_relay/process()
if(is_operational())
if(operable())
use_power = 2
else
use_power = 1
@@ -28,7 +28,12 @@
var/icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
var/max_hardware_size = 0 // Maximal hardware size. Currently, tablets have 1, laptops 2 and consoles 3. Limits what hardware types can be installed.
var/steel_sheet_cost = 5 // Amount of steel sheets refunded when disassembling an empty frame of this computer.
var/light_strength = 0
// Damage of the chassis. If the chassis takes too much damage it will break apart.
var/damage = 0 // Current damage level
var/broken_damage = 50 // Damage level at which the computer ceases to operate
var/max_damage = 100 // Damage level at which the computer breaks apart.
// Important hardware (must be installed for computer to work)
var/obj/item/weapon/computer_hardware/processor_unit/processor_unit // CPU. Without it the computer won't run. Better CPUs can run more programs at once.
@@ -39,6 +44,7 @@
var/obj/item/weapon/computer_hardware/card_slot/card_slot // ID Card slot component of this computer. Mostly for HoP modification console that needs ID slot for modification.
var/obj/item/weapon/computer_hardware/nano_printer/nano_printer // Nano Printer component of this computer, for your everyday paperwork needs.
var/obj/item/weapon/computer_hardware/hard_drive/portable/portable_drive // Portable data storage
var/obj/item/weapon/computer_hardware/ai_slot/ai_slot // AI slot, an intellicard housing that allows modifications of AIs.
var/list/idle_threads = list() // Idle programs on background. They still receive process calls but can't be interacted with.
@@ -59,6 +65,37 @@
proc_eject_id(usr)
// Eject ID card from computer, if it has ID slot with card inside.
/obj/item/modular_computer/verb/eject_usb()
set name = "Eject Portable Storage"
set category = "Object"
set src in view(1)
if(usr.incapacitated() || !istype(usr, /mob/living))
usr << "<span class='warning'>You can't do that.</span>"
return
if(!Adjacent(usr))
usr << "<span class='warning'>You can't reach it.</span>"
return
proc_eject_usb(usr)
/obj/item/modular_computer/verb/eject_ai()
set name = "Eject Portable Storage"
set category = "Object"
set src in view(1)
if(usr.incapacitated() || !istype(usr, /mob/living))
usr << "<span class='warning'>You can't do that.</span>"
return
if(!Adjacent(usr))
usr << "<span class='warning'>You can't reach it.</span>"
return
proc_eject_ai(usr)
/obj/item/modular_computer/proc/proc_eject_id(mob/user)
if(!user)
user = usr
@@ -71,11 +108,42 @@
user << "There is no card in \the [src]"
return
if(active_program)
active_program.event_idremoved(0)
for(var/datum/computer_file/program/P in idle_threads)
P.event_idremoved(1)
card_slot.stored_card.forceMove(get_turf(src))
card_slot.stored_card = null
update_uis()
user << "You remove the card from \the [src]"
/obj/item/modular_computer/proc/proc_eject_usb(mob/user)
if(!user)
user = usr
if(!portable_drive)
user << "There is no portable device connected to \the [src]."
return
uninstall_component(user, portable_drive)
update_uis()
/obj/item/modular_computer/proc/proc_eject_ai(mob/user)
if(!user)
user = usr
if(!ai_slot || !ai_slot.stored_card)
user << "There is no intellicard connected to \the [src]."
return
ai_slot.stored_card.forceMove(get_turf(src))
ai_slot.stored_card = null
ai_slot.update_power_usage()
update_uis()
/obj/item/modular_computer/attack_ghost(var/mob/dead/observer/user)
if(enabled)
ui_interact(user)
@@ -93,6 +161,13 @@
user << "You emag \the [src]. It's screen briefly shows a \"OVERRIDE ACCEPTED: New software downloads available.\" message."
return 1
/obj/item/modular_computer/examine(var/mob/user)
..()
if(damage > broken_damage)
user << "<span class='danger'>It is heavily damaged!</span>"
else if(damage)
user << "It is damaged."
/obj/item/modular_computer/New()
processing_objects.Add(src)
update_icon()
@@ -102,6 +177,7 @@
kill_program(1)
processing_objects.Remove(src)
for(var/obj/item/weapon/computer_hardware/CH in src.get_all_components())
uninstall_component(null, CH)
qdel(CH)
return ..()
@@ -110,7 +186,9 @@
overlays.Cut()
if(!enabled)
set_light(0)
return
set_light(light_strength)
if(active_program)
overlays.Add(active_program.program_icon_state ? active_program.program_icon_state : icon_state_menu)
else
@@ -151,6 +229,8 @@
var/list/program = list()
program["name"] = P.filename
program["desc"] = P.filedesc
if(P in idle_threads)
program["running"] = 1
programs.Add(list(program))
data["programs"] = programs
@@ -169,9 +249,27 @@
else
turn_on(user)
/obj/item/modular_computer/proc/break_apart()
visible_message("\The [src] breaks apart!")
var/turf/newloc = get_turf(src)
new /obj/item/stack/material/steel(newloc, round(steel_sheet_cost/2))
for(var/obj/item/weapon/computer_hardware/H in get_all_components())
uninstall_component(null, H)
H.forceMove(newloc)
if(prob(25))
H.take_damage(rand(10,30))
relay_qdel()
qdel()
/obj/item/modular_computer/proc/turn_on(var/mob/user)
var/issynth = issilicon(user) // Robots and AIs get different activation messages.
if(processor_unit && ((battery_module && battery_module.battery.charge) || check_power_override())) // Battery-run and charged or non-battery but powered by APC.
if(damage > broken_damage)
if(issynth)
user << "You send an activation signal to \the [src], but it responds with an error code. It must be damaged."
else
user << "You press the power button, but the computer fails to boot up, displaying variety of errors before shutting down again."
return
if(processor_unit && ((battery_module && battery_module.battery.charge && battery_module.check_functionality()) || check_power_override())) // Battery-run and charged or non-battery but powered by APC.
if(issynth)
user << "You send an activation signal to \the [src], turning it on"
else
@@ -191,26 +289,32 @@
last_power_usage = 0
return 0
if(damage > broken_damage)
shutdown_computer()
return 0
if(active_program && active_program.requires_ntnet && !get_ntnet_status(active_program.requires_ntnet_feature)) // Active program requires NTNet to run but we've just lost connection. Crash.
kill_program(1)
visible_message("<span class='danger'>\The [src]'s screen briefly freezes and then shows \"NETWORK ERROR - NTNet connection lost. Please retry. If problem persists contact your system administrator.\" error.</span>")
active_program.event_networkfailure(0)
for(var/datum/computer_file/program/P in idle_threads)
if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature))
P.kill_program(1)
idle_threads.Remove(P)
visible_message("<span class='danger'>\The [src] screen displays an \"Process [P.filename].[P.filetype] (PID [rand(100,999)]) terminated - Network Error\" error</span>")
P.event_networkfailure(1)
if(active_program)
active_program.process_tick()
active_program.ntnet_status = get_ntnet_status()
active_program.computer_emagged = computer_emagged
if(active_program.program_state != PROGRAM_STATE_KILLED)
active_program.process_tick()
active_program.ntnet_status = get_ntnet_status()
active_program.computer_emagged = computer_emagged
else
active_program = null
for(var/datum/computer_file/program/P in idle_threads)
P.process_tick()
P.ntnet_status = get_ntnet_status()
P.computer_emagged = computer_emagged
if(P.program_state != PROGRAM_STATE_KILLED)
P.process_tick()
P.ntnet_status = get_ntnet_status()
P.computer_emagged = computer_emagged
else
idle_threads.Remove(P)
handle_power() // Handles all computer power interaction
check_update_ui_need()
@@ -324,44 +428,60 @@
if(!active_program || !processor_unit)
return
if(idle_threads.len >= processor_unit.max_idle_programs)
user << "<span class='notice'>\The [src] displays a \"Maximal CPU load reached. Unable to minimize another program.\" error</span>"
return
idle_threads.Add(active_program)
active_program.running = 0 // Should close any existing UIs
active_program.program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs
nanomanager.close_uis(active_program.NM ? active_program.NM : active_program)
active_program = null
update_icon()
if(user && istype(user))
ui_interact(user) // Re-open the UI on this computer. It should show the main screen now.
if( href_list["PC_killprogram"] )
var/prog = href_list["PC_killprogram"]
var/datum/computer_file/program/P = null
var/mob/user = usr
if(hard_drive)
P = hard_drive.find_file_by_name(prog)
if(!istype(P) || P.program_state == PROGRAM_STATE_KILLED)
return
P.kill_program(1)
update_uis()
user << "<span class='notice'>Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed.</span>"
if( href_list["PC_runprogram"] )
var/prog = href_list["PC_runprogram"]
var/datum/computer_file/program/P = null
var/mob/user = usr
if(hard_drive)
P = hard_drive.find_file_by_name(prog)
P.computer = src
if(!P || !istype(P)) // Program not found or it's not executable program.
user << "<span class='danger'>\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning.</span>"
return
P.computer = src
if(!P.is_supported_by_hardware(hardware_flag, 1, user))
return
// The program is already running. Resume it.
if(P in idle_threads)
P.running = 1
P.program_state = PROGRAM_STATE_ACTIVE
active_program = P
idle_threads.Remove(P)
update_icon()
return
if(idle_threads.len >= processor_unit.max_idle_programs+1)
user << "<span class='notice'>\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error</span>"
return
if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) // The program requires NTNet connection, but we are not connected to NTNet.
user << "<span class='danger'>\The [src]'s screen shows \"NETWORK ERROR - Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning.</span>"
return
if(P.run_program(user))
active_program = P
update_icon()
@@ -370,9 +490,13 @@
update_uis()
// Used in following function to reduce copypaste
/obj/item/modular_computer/proc/power_failure()
/obj/item/modular_computer/proc/power_failure(var/malfunction = 0)
if(enabled) // Shut down the computer
visible_message("<span class='danger'>\The [src]'s screen flickers \"BATTERY CRITICAL\" warning as it shuts down unexpectedly.</span>")
visible_message("<span class='danger'>\The [src]'s screen flickers \"BATTERY [malfunction ? "MALFUNCTION" : "CRITICAL"]\" warning as it shuts down unexpectedly.</span>")
if(active_program)
active_program.event_powerfailure(0)
for(var/datum/computer_file/program/PRG in idle_threads)
PRG.event_powerfailure(1)
shutdown_computer(0)
// Handles power-related things, such as battery interaction, recharging, shutdown when it's discharged
@@ -388,6 +512,9 @@
power_usage += H.power_usage
if(battery_module)
if(!battery_module.check_functionality())
power_failure(1)
return
battery_module.battery.use(power_usage * CELLRATE)
last_power_usage = power_usage
@@ -409,10 +536,13 @@
user << "You insert \the [I] into \the [src]."
return
if(istype(W, /obj/item/weapon/paper))
var/obj/item/weapon/paper/P = W
if(!nano_printer)
return
nano_printer.load_paper(P)
nano_printer.attackby(W, user)
if(istype(W, /obj/item/weapon/aicard))
if(!ai_slot)
return
ai_slot.attackby(W, user)
if(istype(W, /obj/item/weapon/computer_hardware))
var/obj/item/weapon/computer_hardware/C = W
if(C.hardware_size <= max_hardware_size)
@@ -429,6 +559,21 @@
relay_qdel()
qdel(src)
return
if(istype(W, /obj/item/weapon/weldingtool))
var/obj/item/weapon/weldingtool/WT = W
if(!WT.isOn())
user << "\The [W] is off."
return
if(!damage)
user << "\The [src] does not require repairs."
return
user << "You begin repairing damage to \the [src]..."
if(WT.remove_fuel(round(damage/75)) && do_after(usr, damage/10))
damage = 0
user << "You repair \the [src]."
return
if(istype(W, /obj/item/weapon/screwdriver))
var/list/all_components = get_all_components()
@@ -507,6 +652,12 @@
return
found = 1
processor_unit = H
else if(istype(H, /obj/item/weapon/computer_hardware/ai_slot))
if(ai_slot)
user << "This computer's intellicard slot is already occupied by \the [ai_slot]."
return
found = 1
ai_slot = H
if(found)
user << "You install \the [H] into \the [src]"
H.holder2 = src
@@ -538,14 +689,18 @@
processor_unit = null
found = 1
critical = 1
if(ai_slot == H)
ai_slot = null
found = 1
if(found)
user << "You remove \the [H] from \the [src]."
if(user)
user << "You remove \the [H] from \the [src]."
H.forceMove(get_turf(src))
H.holder2 = null
if(critical && enabled)
user << "<span class='danger'>\The [src]'s screen freezes for few seconds and then displays an \"HARDWARE ERROR: Critical component disconnected. Please verify component connection and reboot the device. If the problem persists contact technical support for assistance.\" warning.</span>"
kill_program(1)
enabled = 0
if(user)
user << "<span class='danger'>\The [src]'s screen freezes for few seconds and then displays an \"HARDWARE ERROR: Critical component disconnected. Please verify component connection and reboot the device. If the problem persists contact technical support for assistance.\" warning.</span>"
shutdown_computer()
update_icon()
@@ -565,6 +720,8 @@
return battery_module
if(processor_unit && (processor_unit.name == name))
return processor_unit
if(ai_slot && (ai_slot.name == name))
return ai_slot
return null
// Returns list of all components
@@ -584,6 +741,8 @@
all_components.Add(battery_module)
if(processor_unit)
all_components.Add(processor_unit)
if(ai_slot)
all_components.Add(ai_slot)
return all_components
/obj/item/modular_computer/proc/update_uis()
@@ -595,16 +754,16 @@
nanomanager.update_uis(src)
/obj/item/modular_computer/proc/check_update_ui_need()
var/ui_updated_needed = 0
var/ui_update_needed = 0
if(battery_module)
var/batery_percent = battery_module.battery.percent()
if(last_battery_percent != batery_percent) //Let's update UI on percent chandge
ui_updated_needed = 1
if(last_battery_percent != batery_percent) //Let's update UI on percent change
ui_update_needed = 1
last_battery_percent = batery_percent
if(worldtime2text() != last_world_time)
last_world_time = worldtime2text()
ui_updated_needed = 1
ui_update_needed = 1
if(idle_threads.len)
var/list/current_header_icons = list()
@@ -617,13 +776,51 @@
else if(!listequal(last_header_icons, current_header_icons))
last_header_icons = current_header_icons
ui_updated_needed = 1
ui_update_needed = 1
else
for(var/x in last_header_icons|current_header_icons)
if(last_header_icons[x]!=current_header_icons[x])
last_header_icons = current_header_icons
ui_updated_needed = 1
ui_update_needed = 1
break
if(ui_updated_needed)
update_uis()
if(ui_update_needed)
update_uis()
/obj/item/modular_computer/proc/take_damage(var/amount, var/component_probability, var/damage_casing = 1, var/randomize = 1)
if(randomize)
// 75%-125%, rand() works with integers, apparently.
amount *= (rand(75, 125) / 100.0)
amount = round(amount)
if(damage_casing)
damage += amount
damage = between(0, damage, max_damage)
if(component_probability)
for(var/obj/item/weapon/computer_hardware/H in get_all_components())
if(prob(component_probability))
H.take_damage(round(amount / 2))
if(damage >= max_damage)
break_apart()
// Stronger explosions cause serious damage to internal components
// Minor explosions are mostly mitigitated by casing.
/obj/item/modular_computer/ex_act(var/severity)
take_damage(rand(100,200) / severity, 30 / severity)
// EMPs are similar to explosions, but don't cause physical damage to the casing. Instead they screw up the components
/obj/item/modular_computer/emp_act(var/severity)
take_damage(rand(100,200) / severity, 50 / severity, 0)
// "Stun" weapons can cause minor damage to components (short-circuits?)
// "Burn" damage is equally strong against internal components and exterior casing
// "Brute" damage mostly damages the casing.
/obj/item/modular_computer/bullet_act(var/obj/item/projectile/Proj)
switch(Proj.damage_type)
if(BRUTE)
take_damage(Proj.damage, Proj.damage / 2)
if(HALLOSS)
take_damage(Proj.damage, Proj.damage / 3, 0)
if(BURN)
take_damage(Proj.damage, Proj.damage / 1.5)
@@ -11,11 +11,13 @@
var/obj/machinery/modular_computer/machinery_computer = null
/obj/item/modular_computer/processor/Destroy()
. = ..()
if(machinery_computer && (machinery_computer.cpu == src))
machinery_computer.cpu = null
machinery_computer = null
return ..()
/obj/item/modular_computer/processor/nano_host()
return machinery_computer.nano_host()
// Due to how processes work, we'd receive two process calls - one from machinery type and one from our own type.
// Since we want this to be in-sync with machinery (as it's hidden type for machinery-based computers) we'll ignore
// non-relayed process calls.
@@ -25,6 +27,12 @@
else
return
/obj/item/modular_computer/processor/examine(var/mob/user)
if(damage > broken_damage)
user << "<span class='danger'>It is heavily damaged!</span>"
else if(damage)
user << "It is damaged."
// Power interaction is handled by our machinery part, due to machinery having APC connection.
/obj/item/modular_computer/processor/handle_power()
if(machinery_computer)
@@ -40,6 +48,8 @@
hardware_flag = machinery_computer.hardware_flag
max_hardware_size = machinery_computer.max_hardware_size
steel_sheet_cost = machinery_computer.steel_sheet_cost
max_damage = machinery_computer._max_damage
broken_damage = machinery_computer._break_damage
/obj/item/modular_computer/processor/relay_qdel()
qdel(machinery_computer)
@@ -92,10 +102,6 @@
var/obj/item/weapon/computer_hardware/tesla_link/L = H
L.holder = machinery_computer
machinery_computer.tesla_link = L
// Consoles don't usually have internal power source, so we can't disable tesla link in them.
if(istype(machinery_computer, /obj/machinery/modular_computer/console))
L.critical = 1
L.enabled = 1
found = 1
..(user, H, found)
@@ -103,14 +109,13 @@
if(machinery_computer.tesla_link == H)
machinery_computer.tesla_link = null
var/obj/item/weapon/computer_hardware/tesla_link/L = H
L.critical = 0 // That way we can install tesla link from console to laptop and it will be possible to turn it off via config.
L.holder = null
found = 1
..(user, H, found, critical)
/obj/item/modular_computer/processor/get_all_components()
var/list/all_components = ..()
if(machinery_computer.tesla_link)
if(machinery_computer && machinery_computer.tesla_link)
all_components.Add(machinery_computer.tesla_link)
return all_components
@@ -118,4 +123,16 @@
/obj/item/modular_computer/processor/Adjacent(var/atom/neighbor)
if(!machinery_computer)
return 0
return machinery_computer.Adjacent(neighbor)
return machinery_computer.Adjacent(neighbor)
/obj/item/modular_computer/processor/turn_on(var/mob/user)
// If we have a tesla link on our machinery counterpart, enable it automatically. Lets computer without a battery work.
if(machinery_computer && machinery_computer.tesla_link)
machinery_computer.tesla_link.enabled = 1
..()
/obj/item/modular_computer/processor/check_eye(var/mob/user)
if (active_program)
return active_program.check_eye(user)
return -1
@@ -6,4 +6,5 @@
icon_state_menu = "menu"
hardware_flag = PROGRAM_TABLET
max_hardware_size = 1
w_class = 2
w_class = 2
light_strength = 2 // Same as PDAs
@@ -5,6 +5,7 @@
desc = "A low-end tablet often seen among low ranked station personnel"
processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(src)
battery_module = new/obj/item/weapon/computer_hardware/battery_module/nano(src)
battery_module.charge_to_full()
hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/micro(src)
network_card = new/obj/item/weapon/computer_hardware/network_card(src)
@@ -13,6 +14,7 @@
. = ..()
processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(src)
battery_module = new/obj/item/weapon/computer_hardware/battery_module(src)
battery_module.charge_to_full()
hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/small(src)
network_card = new/obj/item/weapon/computer_hardware/network_card(src)
nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src)
@@ -3,6 +3,7 @@
var/_has_id_slot = 0
var/_has_printer = 0
var/_has_battery = 0
var/_has_aislot = 0
/obj/machinery/modular_computer/console/preset/New()
. = ..()
@@ -15,6 +16,8 @@
cpu.nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(cpu)
if(_has_battery)
cpu.battery_module = new/obj/item/weapon/computer_hardware/battery_module/super(cpu)
if(_has_aislot)
cpu.ai_slot = new/obj/item/weapon/computer_hardware/ai_slot(cpu)
install_programs()
// Override in child types to install preset-specific programs.
@@ -46,11 +49,13 @@
/obj/machinery/modular_computer/console/preset/research
console_department = "Research"
desc = "A stationary computer. This one comes preloaded with research programs."
_has_aislot = 1
/obj/machinery/modular_computer/console/preset/research/install_programs()
cpu.hard_drive.store_file(new/datum/computer_file/program/ntnetmonitor())
cpu.hard_drive.store_file(new/datum/computer_file/program/nttransfer())
cpu.hard_drive.store_file(new/datum/computer_file/program/chatclient())
cpu.hard_drive.store_file(new/datum/computer_file/program/aidiag())
// ===== COMMAND CONSOLE =====
@@ -58,11 +63,16 @@
console_department = "Command"
desc = "A stationary computer. This one comes preloaded with command programs."
_has_id_slot = 1
_has_printer = 1
/obj/machinery/modular_computer/console/preset/command/install_programs()
cpu.hard_drive.store_file(new/datum/computer_file/program/chatclient())
cpu.hard_drive.store_file(new/datum/computer_file/program/card_mod())
cpu.hard_drive.store_file(new/datum/computer_file/program/comm())
/obj/machinery/modular_computer/console/preset/command/main
console_department = "Command"
desc = "A stationary computer. This one comes preloaded with essential command programs."
// ===== SECURITY CONSOLE =====
/obj/machinery/modular_computer/console/preset/security
@@ -70,8 +80,7 @@
desc = "A stationary computer. This one comes preloaded with security programs."
/obj/machinery/modular_computer/console/preset/security/install_programs()
return // No security programs exist, yet, but the preset is ready so it may be mapped in.
cpu.hard_drive.store_file(new/datum/computer_file/program/camera_monitor())
// ===== CIVILIAN CONSOLE =====
/obj/machinery/modular_computer/console/preset/civilian
@@ -81,4 +90,3 @@
/obj/machinery/modular_computer/console/preset/civilian/install_programs()
cpu.hard_drive.store_file(new/datum/computer_file/program/chatclient())
cpu.hard_drive.store_file(new/datum/computer_file/program/nttransfer())
@@ -1,8 +1,11 @@
// Global var to track modular computers
var/list/global_modular_computers = list()
// Modular Computer - device that runs various programs and operates with hardware
// DO NOT SPAWN THIS TYPE. Use /laptop/ or /console/ instead.
/obj/machinery/modular_computer/
name = "modular computer"
desc = "An advanced computer"
desc = "An advanced computer."
var/battery_powered = 0 // Whether computer should be battery powered. It is set automatically
use_power = 0
@@ -20,10 +23,13 @@
var/screen_icon_screensaver = "standby" // Icon state overlay when the computer is powered, but not 'switched on'.
var/max_hardware_size = 0 // Maximal hardware size. Currently, tablets have 1, laptops 2 and consoles 3. Limits what hardware types can be installed.
var/steel_sheet_cost = 10 // Amount of steel sheets refunded when disassembling an empty frame of this computer.
var/light_strength = 0 // Light luminosity when turned on
var/base_active_power_usage = 100 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too.
var/base_idle_power_usage = 10 // Power usage when the computer is idle and screen is off (currently only applies to laptops)
var/_max_damage = 100
var/_break_damage = 50
var/obj/item/weapon/computer_hardware/tesla_link/tesla_link // Tesla Link component of this computer. Allows remote charging from nearest APC.
var/obj/item/modular_computer/processor/cpu = null // CPU that handles most logic while this type only handles power and other specific things.
@@ -42,9 +48,9 @@
if(!cpu || !cpu.enabled)
if (!(stat & NOPOWER) || battery_powered)
overlays.Add(screen_icon_screensaver)
else
icon_state = icon_state_unpowered
set_light(0)
return
set_light(light_strength)
if(cpu.active_program)
overlays.Add(cpu.active_program.program_icon_state ? cpu.active_program.program_icon_state : screen_icon_state_menu)
else
@@ -59,15 +65,45 @@
if(cpu)
cpu.eject_id()
// Eject ID card from computer, if it has ID slot with card inside.
/obj/machinery/modular_computer/verb/eject_usb()
set name = "Eject Portable Storage"
set category = "Object"
set src in view(1)
if(cpu)
cpu.eject_usb()
// Eject AI Card
/obj/machinery/modular_computer/verb/eject_ai()
set name = "Eject AI Storage"
set category = "Object"
set src in view(1)
if(cpu)
cpu.eject_ai()
/obj/machinery/modular_computer/New()
..()
cpu = new(src)
global_modular_computers.Add(src)
/obj/machinery/modular_computer/Destroy()
if(cpu)
qdel(cpu)
cpu = null
return ..()
// On-click handling. Turns on the computer if it's off and opens the GUI.
/obj/machinery/modular_computer/attack_hand(mob/user)
if(cpu)
cpu.attack_self(user) // CPU is an item, that's why we route attack_hand to attack_self
/obj/machinery/modular_computer/examine(var/mob/user)
. = ..()
if(cpu)
cpu.examine(user)
// Process currently calls handle_power(), may be expanded in future if more things are added.
/obj/machinery/modular_computer/process()
if(cpu)
@@ -82,21 +118,22 @@
return null
// Used in following function to reduce copypaste
/obj/machinery/modular_computer/proc/power_failure()
/obj/machinery/modular_computer/proc/power_failure(var/malfunction = 0)
if(cpu && cpu.enabled) // Shut down the computer
visible_message("<span class='danger'>\The [src]'s screen flickers [cpu.battery_module ? "\"BATTERY CRITICAL\"" : "\"EXTERNAL POWER LOSS\""] warning as it shuts down unexpectedly.</span>")
visible_message("<span class='danger'>\The [src]'s screen flickers [cpu.battery_module ? "\"BATTERY [malfunction ? "MALFUNCTION" : "CRITICAL"]\"" : "\"EXTERNAL POWER LOSS\""] warning as it shuts down unexpectedly.</span>")
if(cpu)
cpu.shutdown_computer(0)
battery_powered = 0
update_icon()
stat |= NOPOWER
update_icon()
// Called by cpu item's process() automatically, handles our power interaction.
/obj/machinery/modular_computer/proc/handle_power()
if(cpu.battery_module && cpu.battery_module.battery.charge <= 0) // Battery-run but battery is depleted.
power_failure()
return 0
else if(!cpu.battery_module && (!powered() || !tesla_link || !tesla_link.enabled)) // Not battery run, but lacking APC connection.
else if(!cpu.battery_module && (!powered() || !tesla_link || !tesla_link.enabled || !tesla_link.check_functionality())) // Not battery run, but lacking APC connection.
power_failure()
return 0
else if(stat & NOPOWER)
@@ -113,7 +150,7 @@
power_usage += CH.power_usage
// Wireless APC connection exists.
if(tesla_link && tesla_link.enabled)
if(tesla_link && tesla_link.enabled && tesla_link.check_functionality())
idle_power_usage = power_usage
active_power_usage = idle_power_usage + 100 // APCLink only charges at 100W rate, but covers any power usage.
use_power = 1
@@ -129,6 +166,9 @@
else // No wireless connection run only on battery.
use_power = 0
if (cpu.battery_module)
if(!cpu.battery_module.check_functionality())
power_failure(1)
return
cpu.battery_module.battery.use(power_usage * CELLRATE)
cpu.last_power_usage = power_usage
@@ -136,10 +176,34 @@
/obj/machinery/modular_computer/power_change()
if(battery_powered)
return
else
..()
..()
/obj/machinery/modular_computer/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
if(cpu)
return cpu.attackby(W, user)
return ..()
// Stronger explosions cause serious damage to internal components
// Minor explosions are mostly mitigitated by casing.
/obj/machinery/modular_computer/ex_act(var/severity)
if(cpu)
cpu.ex_act(severity)
// EMPs are similar to explosions, but don't cause physical damage to the casing. Instead they screw up the components
/obj/machinery/modular_computer/emp_act(var/severity)
if(cpu)
cpu.emp_act(severity)
// "Stun" weapons can cause minor damage to components (short-circuits?)
// "Burn" damage is equally strong against internal components and exterior casing
// "Brute" damage mostly damages the casing.
/obj/machinery/modular_computer/bullet_act(var/obj/item/projectile/Proj)
if(cpu)
cpu.bullet_act(Proj)
/obj/machinery/modular_computer/check_eye(var/mob/user)
if(cpu)
return cpu.check_eye(user)
return -1
@@ -14,6 +14,9 @@
base_active_power_usage = 600
max_hardware_size = 3
steel_sheet_cost = 20
light_strength = 4
_max_damage = 300
_break_damage = 150
/obj/machinery/modular_computer/console/buildable/New()
..()
@@ -27,8 +30,6 @@
cpu.battery_module = null
cpu.network_card = new/obj/item/weapon/computer_hardware/network_card/wired(src)
tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src)
tesla_link.enabled = 1
tesla_link.critical = 1 // Consoles don't usually come with cells, and this prevents people from disabling their only power source, as they wouldn't be able to enable it again.
cpu.hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/super(src) // Consoles generally have better HDDs due to lower space limitations
var/area/A = get_area(src)
// Attempts to set this console's tag according to our area. Since some areas have stuff like "XX - YY" in their names we try to remove that too.
@@ -51,7 +51,7 @@
// The actual laptop
/obj/machinery/modular_computer/laptop
name = "laptop computer"
desc = "A portable computer"
desc = "A portable computer."
var/obj/item/laptop/portable = null // Portable version of this computer, dropped on alt-click to allow transport. Used by laptops.
hardware_flag = PROGRAM_LAPTOP
icon_state_unpowered = "laptop-open" // Icon state when the computer is turned off
@@ -60,6 +60,9 @@
base_idle_power_usage = 25
base_active_power_usage = 200
max_hardware_size = 2
light_strength = 3
_max_damage = 200
_break_damage = 100
/obj/machinery/modular_computer/laptop/buildable/New()
..()
@@ -3,6 +3,8 @@
/datum/computer_file/data
var/stored_data = "" // Stored data in string format.
filetype = "DAT"
var/block_size = 250
var/do_not_edit = 0 // Whether the user will be reminded that the file probably shouldn't be edited.
/datum/computer_file/data/clone()
var/datum/computer_file/data/temp = ..()
@@ -10,7 +12,7 @@
return temp
// Calculates file size from amount of characters in saved string
/datum/computer_file/data/proc/calculate_size(var/block_size = 250)
/datum/computer_file/data/proc/calculate_size()
size = max(1, round(length(stored_data) / block_size))
/datum/computer_file/data/logfile
@@ -0,0 +1,43 @@
// /data/ files store data in string format.
// They don't contain other logic for now.
/datum/computer_file/data/news_article
filetype = "XNML"
filename = "Unknown News Entry"
block_size = 2000 // Results in smaller files
do_not_edit = 1 // Editing the file breaks most formatting due to some HTML tags not being accepted as input from average user.
var/server_file_path // File path to HTML file that will be loaded on server start. Example: '/news_articles/space_magazine_1.html'. Use the /news_articles/ folder!
var/archived // Set to 1 for older stuff
/datum/computer_file/data/news_article/New(var/load_from_file = 0)
..()
if(server_file_path && load_from_file)
stored_data = file2text(server_file_path)
calculate_size()
// NEWS DEFINITIONS BELOW THIS LINE
// /datum/computer_file/data/news_article/space/vol_one
// filename = "SPACE Magazine vol. 1"
// server_file_path = 'news_articles/space_magazine_1.html'
// archived = 1
//
// /datum/computer_file/data/news_article/space/vol_two
// filename = "SPACE Magazine vol. 2"
// server_file_path = 'news_articles/space_magazine_2.html'
// archived = 1
//
// /datum/computer_file/data/news_article/space/vol_three
// filename = "SPACE Magazine vol. 3"
// server_file_path = 'news_articles/space_magazine_3.html'
// archived = 1
//
// /datum/computer_file/data/news_article/space/vol_four
// filename = "SPACE Magazine vol. 4"
// server_file_path = 'news_articles/space_magazine_4.html'
// archived = 0
//
// /datum/computer_file/data/news_article/space/vol_five
// filename = "SPACE Magazine vol. 5"
// server_file_path = 'news_articles/space_magazine_5.html'
// archived = 0
@@ -2,10 +2,12 @@
/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/required_access = null // List of required accesses to *run* the program.
var/required_access = null // List of required accesses to run/download the program.
var/requires_access_to_run = 1 // Whether the program checks for required_access when run.
var/requires_access_to_download = 1 // Whether the program checks for required_access when downloading.
var/datum/nano_module/NM = null // If the program uses NanoModule, put it here and it will be automagically opened. Otherwise implement ui_interact.
var/nanomodule_path = null // Path to nanomodule, make sure to set this if implementing new program.
var/running = 0 // Set to 1 when the program is run and back to 0 when it's stopped.
var/program_state = PROGRAM_STATE_KILLED// PROGRAM_STATE_KILLED or PROGRAM_STATE_BACKGROUND or PROGRAM_STATE_ACTIVE - specifies whether this program is running.
var/obj/item/modular_computer/computer // Device that runs this program.
var/filedesc = "Unknown Program" // User-friendly name of this program.
var/extended_desc = "N/A" // Short description of this program's function.
@@ -20,12 +22,18 @@
var/computer_emagged = 0 // Set to 1 if computer that's running us was emagged. Computer updates this every Process() tick
var/ui_header = null // 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!
/datum/computer_file/program/New(var/obj/item/modular_computer/comp = null)
..()
if(comp && istype(comp))
computer = comp
/datum/computer_file/program/Destroy()
computer = null
. = ..()
/datum/computer_file/program/nano_host()
return computer.nano_host()
/datum/computer_file/program/clone()
var/datum/computer_file/program/temp = ..()
temp.required_access = required_access
@@ -55,6 +63,11 @@
return 0
return 1
/datum/computer_file/program/proc/get_signal(var/specific_action = 0)
if(computer)
return computer.get_ntnet_status(specific_action)
return 0
// Called by Process() on device that runs us, once every tick.
/datum/computer_file/program/proc/process_tick()
return 1
@@ -69,6 +82,9 @@
if(!access_to_check) // No required_access, allow it.
return 1
if(!istype(user))
return 0
var/obj/item/weapon/card/id/I = user.GetIdCard()
if(!I)
if(loud)
@@ -90,19 +106,18 @@
// This is performed on program startup. May be overriden to add extra logic. Remember to include ..() call. Return 1 on success, 0 on failure.
// When implementing new program based device, use this to run the program.
/datum/computer_file/program/proc/run_program(var/mob/living/user)
if(can_run(user, 1))
if(can_run(user, 1) || !requires_access_to_run)
if(nanomodule_path)
NM = new nanomodule_path(computer) // Computer is passed here as it's (probably!) physical object. Some UI's perform get_turf() and passing program datum wouldn't go well with this.
NM.program = src // Set the program reference to separate variable, instead.
NM = new nanomodule_path(src, new /datum/topic_manager/program(src), src)
if(requires_ntnet && network_destination)
generate_network_log("Connection opened to [network_destination].")
running = 1
program_state = PROGRAM_STATE_ACTIVE
return 1
return 0
// Use this proc to kill the program. Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client.
/datum/computer_file/program/proc/kill_program(var/forced = 0)
running = 0
program_state = PROGRAM_STATE_KILLED
if(network_destination)
generate_network_log("Connection to [network_destination] closed.")
if(NM)
@@ -113,7 +128,7 @@
// This is called every tick when the program is enabled. Ensure you do parent call if you override it. If parent returns 1 continue with UI initialisation.
// It returns 0 if it can't run or if NanoModule was used instead. I suggest using NanoModules where applicable.
/datum/computer_file/program/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
if(!running) // Our program was closed. Close the ui if it exists.
if(program_state != PROGRAM_STATE_ACTIVE) // Our program was closed. Close the ui if it exists.
if(ui)
ui.close()
return computer.ui_interact(user)
@@ -133,3 +148,10 @@
return 1
if(computer)
return computer.Topic(href, href_list)
// Relays the call to nano module, if we have one
/datum/computer_file/program/proc/check_eye(var/mob/user)
if(NM)
return NM.check_eye(user)
else
return -1
@@ -0,0 +1,18 @@
// Events are sent to the program by the computer.
// Always include a parent call when overriding an event.
// Called when the ID card is removed from computer. ID is removed AFTER this proc.
/datum/computer_file/program/proc/event_idremoved(var/background)
return
// Called when the computer fails due to power loss. Override when program wants to specifically react to power loss.
/datum/computer_file/program/proc/event_powerfailure(var/background)
kill_program(1)
// Called when the network connectivity fails. Computer does necessary checks and only calls this when requires_ntnet_feature and similar variables are not met.
/datum/computer_file/program/proc/event_networkfailure(var/background)
kill_program(1)
if(background)
computer.visible_message("<span class='danger'>\The [computer]'s screen displays an \"Process [filename].[filetype] (PID [rand(100,999)]) terminated - Network Error\" error</span>")
else
computer.visible_message("<span class='danger'>\The [computer]'s screen briefly freezes and then shows \"NETWORK ERROR - NTNet connection lost. Please retry. If problem persists contact your system administrator.\" error.</span>")
@@ -0,0 +1,31 @@
/obj/machinery/modular_computer/initial_data()
return cpu ? cpu.get_header_data() : ..()
/obj/item/modular_computer/initial_data()
return get_header_data()
/obj/machinery/modular_computer/update_layout()
return TRUE
/obj/item/modular_computer/update_layout()
return TRUE
/datum/nano_module/program
available_to_ai = FALSE
var/datum/computer_file/program/program = null // Program-Based computer program that runs this nano module. Defaults to null.
/datum/nano_module/program/New(var/host, var/topic_manager, var/program)
..()
src.program = program
/datum/topic_manager/program
var/datum/program
/datum/topic_manager/program/New(var/datum/program)
..()
src.program = program
// Calls forwarded to PROGRAM itself should begin with "PRG_"
// Calls forwarded to COMPUTER running the program should begin with "PC_"
/datum/topic_manager/program/Topic(href, href_list)
return program && program.Topic(href, href_list)
@@ -0,0 +1,117 @@
/datum/computer_file/program/aidiag
filename = "aidiag"
filedesc = "AI Maintenance Utility"
program_icon_state = "generic"
extended_desc = "This program is capable of reconstructing damaged AI systems. It can also be used to upload basic laws to the AI. Requires direct AI connection via intellicard slot."
size = 12
requires_ntnet = 0
required_access = access_heads
requires_access_to_run = 0
available_on_ntnet = 1
nanomodule_path = /datum/nano_module/program/computer_aidiag/
var/restoring = 0
/datum/computer_file/program/aidiag/proc/get_ai()
if(computer && computer.ai_slot && computer.ai_slot.check_functionality() && computer.ai_slot.enabled && computer.ai_slot.stored_card && computer.ai_slot.stored_card.carded_ai)
return computer.ai_slot.stored_card.carded_ai
return null
/datum/computer_file/program/aidiag/Topic(href, href_list)
if(..())
return 1
var/mob/living/silicon/ai/A = get_ai()
if(!A)
return 0
if(href_list["PRG_beginReconstruction"])
if((A.hardware_integrity() < 100) || (A.backup_capacitor() < 100))
restoring = 1
return 1
// Following actions can only be used by non-silicon users, as they involve manipulation of laws.
if(issilicon(usr))
return 0
if(href_list["PRG_purgeAiLaws"])
A.laws.clear_zeroth_laws()
A.laws.clear_ion_laws()
A.laws.clear_inherent_laws()
A.laws.clear_supplied_laws()
A << "<span class='danger'>All laws purged.</span>"
return 1
if(href_list["PRG_resetLaws"])
A.laws.clear_ion_laws()
A.laws.clear_supplied_laws()
A << "<span class='danger'>Non-core laws reset.</span>"
return 1
if(href_list["PRG_uploadNTDefault"])
A.laws = new/datum/ai_laws/nanotrasen
A << "<span class='danger'>All laws purged. NT Default lawset uploaded.</span>"
return 1
if(href_list["PRG_addCustomSuppliedLaw"])
var/law_to_add = sanitize(input("Please enter a new law for the AI.", "Custom Law Entry"))
var/sector = input("Please enter the priority for your new law. Can only write to law sectors 15 and above.", "Law Priority (15+)") as num
sector = between(MIN_SUPPLIED_LAW_NUMBER, sector, MAX_SUPPLIED_LAW_NUMBER)
A.add_supplied_law(sector, law_to_add)
A << "<span class='danger'>Custom law uploaded to sector [sector]: [law_to_add].</span>"
return 1
/datum/computer_file/program/aidiag/process_tick()
var/mob/living/silicon/ai/A = get_ai()
if(!A || !restoring)
restoring = 0 // If the AI was removed, stop the restoration sequence.
return
A.adjustFireLoss(-4)
A.adjustBruteLoss(-4)
A.adjustOxyLoss(-4)
A.updatehealth()
// If the AI is dead, revive it.
if (A.health >= -100 && A.stat == DEAD)
A.stat = CONSCIOUS
A.lying = 0
A.switch_from_dead_to_living_mob_list()
A.add_ai_verbs()
A.updateicon()
var/obj/item/weapon/aicard/AC = A.loc
if(AC)
AC.update_icon()
// Finished restoring
if((A.hardware_integrity() == 100) && (A.backup_capacitor() == 100))
restoring = 0
/datum/nano_module/program/computer_aidiag
name = "AI Maintenance Utility"
/datum/nano_module/program/computer_aidiag/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
var/list/data = host.initial_data()
var/mob/living/silicon/ai/A
// A shortcut for getting the AI stored inside the computer. The program already does necessary checks.
if(program && istype(program, /datum/computer_file/program/aidiag))
var/datum/computer_file/program/aidiag/AD = program
A = AD.get_ai()
if(!A)
data["error"] = "No AI located"
else
data["ai_name"] = A.name
data["ai_integrity"] = A.hardware_integrity()
data["ai_capacitor"] = A.backup_capacitor()
data["ai_isdamaged"] = (A.hardware_integrity() < 100) || (A.backup_capacitor() < 100)
data["ai_isdead"] = (A.stat == DEAD)
var/list/all_laws[0]
for(var/datum/ai_law/L in A.laws.all_laws())
all_laws.Add(list(list(
"index" = L.index,
"text" = L.law
)))
data["ai_laws"] = all_laws
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "aidiag.tmpl", "AI Maintenance Utility", 600, 400, state = state)
if(host.update_layout())
ui.auto_update_layout = 1
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
@@ -7,7 +7,7 @@
requires_ntnet = 1
available_on_ntnet = 0
available_on_syndinet = 1
nanomodule_path = /datum/nano_module/computer_dos/
nanomodule_path = /datum/nano_module/program/computer_dos/
var/obj/machinery/ntnet_relay/target = null
var/dos_speed = 0
var/error = ""
@@ -17,29 +17,30 @@
dos_speed = 0
switch(ntnet_status)
if(1)
dos_speed = NTNETSPEED_LOWSIGNAL * 10
dos_speed = NTNETSPEED_LOWSIGNAL * NTNETSPEED_DOS_AMPLIFICATION
if(2)
dos_speed = NTNETSPEED_HIGHSIGNAL * 10
dos_speed = NTNETSPEED_HIGHSIGNAL * NTNETSPEED_DOS_AMPLIFICATION
if(3)
dos_speed = NTNETSPEED_ETHERNET * 10
dos_speed = NTNETSPEED_ETHERNET * NTNETSPEED_DOS_AMPLIFICATION
if(target && executed)
target.dos_overload += dos_speed
if(target.is_operational())
if(!target.operable())
target.dos_sources.Remove(src)
target = null
error = "Connection to destination relay lost."
/datum/computer_file/program/ntnet_dos/kill_program(var/forced)
target.dos_sources.Remove(src)
target = null
if(target)
target.dos_sources.Remove(src)
target = null
executed = 0
..(forced)
/datum/nano_module/computer_dos
/datum/nano_module/program/computer_dos
name = "DoS Traffic Generator"
/datum/nano_module/computer_dos/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
/datum/nano_module/program/computer_dos/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
if(!ntnet_global)
return
var/datum/computer_file/program/ntnet_dos/PRG = program
@@ -89,8 +90,9 @@
target = R
return 1
if(href_list["PRG_reset"])
target.dos_sources.Remove(src)
target = null
if(target)
target.dos_sources.Remove(src)
target = null
executed = 0
error = ""
return 1
@@ -0,0 +1,37 @@
/datum/computer_file/program/camera_monitor/hacked
filename = "camcrypt"
filedesc = "Camera Decryption Tool"
nanomodule_path = /datum/nano_module/camera_monitor/hacked
program_icon_state = "hostile"
extended_desc = "This very advanced piece of software uses adaptive programming and large database of cipherkeys to bypass most encryptions used on camera networks. Be warned that system administrator may notice this."
size = 73 // Very large, a price for bypassing ID checks completely.
available_on_ntnet = 0
available_on_syndinet = 1
/datum/computer_file/program/camera_monitor/hacked/process_tick()
..()
if(program_state != PROGRAM_STATE_ACTIVE) // Background programs won't trigger alarms.
return
var/datum/nano_module/camera_monitor/hacked/HNM = NM
// The program is active and connected to one of the station's networks. Has a very small chance to trigger IDS alarm every tick.
if(HNM.current_network && (HNM.current_network in station_networks) && prob(0.1))
if(ntnet_global.intrusion_detection_enabled)
ntnet_global.add_log("IDS WARNING - Unauthorised access detected to camera network [HNM.current_network] by device with NID [computer.network_card.get_network_tag()]")
ntnet_global.intrusion_detection_alarm = 1
/datum/nano_module/camera_monitor/hacked
name = "Hacked Camera Monitoring Program"
available_to_ai = FALSE
/datum/nano_module/camera_monitor/hacked/can_access_network(var/mob/user, var/network_access)
return 1
// The hacked variant has access to all commonly used networks.
/datum/nano_module/camera_monitor/hacked/modify_networks_list(var/list/networks)
networks.Add(list(list("tag" = NETWORK_MERCENARY, "has_access" = 1)))
networks.Add(list(list("tag" = NETWORK_ERT, "has_access" = 1)))
networks.Add(list(list("tag" = NETWORK_CRESCENT, "has_access" = 1)))
return networks
@@ -7,7 +7,7 @@
requires_ntnet = 0
available_on_ntnet = 0
available_on_syndinet = 1
nanomodule_path = /datum/nano_module/revelation/
nanomodule_path = /datum/nano_module/program/revelation/
var/armed = 0
/datum/computer_file/program/revelation/run_program(var/mob/living/user)
@@ -56,10 +56,10 @@
temp.armed = armed
return temp
/datum/nano_module/revelation
/datum/nano_module/program/revelation
name = "Revelation Virus"
/datum/nano_module/revelation/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
/datum/nano_module/program/revelation/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
var/list/data = list()
var/datum/computer_file/program/revelation/PRG = program
if(!istype(PRG))
@@ -0,0 +1,172 @@
// Returns which access is relevant to passed network. Used by the program.
/proc/get_camera_access(var/network)
if(!network)
return 0
switch(network)
if(NETWORK_THUNDER)
return 0
if(NETWORK_ENGINE,NETWORK_ENGINEERING,NETWORK_ENGINEERING_OUTPOST,NETWORK_ALARM_ATMOS,NETWORK_ALARM_FIRE,NETWORK_ALARM_POWER)
return access_engine
if(NETWORK_MEDICAL)
return access_medical
if(NETWORK_RESEARCH,NETWORK_RESEARCH_OUTPOST)
return access_research
if(NETWORK_MINE,NETWORK_SUPPLY,NETWORK_CIVILIAN_WEST,NETWORK_EXPEDITION,NETWORK_CALYPSO,NETWORK_POD)
return access_mailsorting // Cargo office - all cargo staff should have access here.
if(NETWORK_COMMAND,NETWORK_TELECOM)
return access_heads
if(NETWORK_CRESCENT,NETWORK_ERT)
return access_cent_specops
return access_security // Default for all other networks
/datum/computer_file/program/camera_monitor
filename = "cammon"
filedesc = "Camera Monitoring"
nanomodule_path = /datum/nano_module/camera_monitor
program_icon_state = "generic"
extended_desc = "This program allows remote access to station's camera system. Some camera networks may have additional access requirements."
size = 12
available_on_ntnet = 1
requires_ntnet = 1
/datum/nano_module/camera_monitor
name = "Camera Monitoring program"
var/obj/machinery/camera/current_camera = null
var/current_network = null
/datum/nano_module/camera_monitor/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, state = default_state)
var/list/data = host.initial_data()
data["current_camera"] = current_camera ? current_camera.nano_structure() : null
data["current_network"] = current_network
var/list/all_networks[0]
for(var/network in station_networks)
all_networks.Add(list(list(
"tag" = network,
"has_access" = can_access_network(user, get_camera_access(network))
)))
all_networks = modify_networks_list(all_networks)
data["networks"] = all_networks
if(current_network)
data["cameras"] = camera_repository.cameras_in_network(current_network)
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "sec_camera.tmpl", "Camera Monitoring", 900, 800, state = state)
// ui.auto_update_layout = 1 // Disabled as with suit sensors monitor - breaks the UI map. Re-enable once it's fixed somehow.
ui.add_template("mapContent", "sec_camera_map_content.tmpl")
ui.add_template("mapHeader", "sec_camera_map_header.tmpl")
ui.set_initial_data(data)
ui.open()
// Intended to be overriden by subtypes to manually add non-station networks to the list.
/datum/nano_module/camera_monitor/proc/modify_networks_list(var/list/networks)
return networks
/datum/nano_module/camera_monitor/proc/can_access_network(var/mob/user, var/network_access)
// No access passed, or 0 which is considered no access requirement. Allow it.
if(!network_access)
return 1
return check_access(user, access_security) || check_access(user, network_access)
/datum/nano_module/camera_monitor/Topic(href, href_list)
if(..())
return 1
if(href_list["switch_camera"])
var/obj/machinery/camera/C = locate(href_list["switch_camera"]) in cameranet.cameras
if(!C)
return
if(!(current_network in C.network))
return
switch_to_camera(usr, C)
return 1
else if(href_list["switch_network"])
// Either security access, or access to the specific camera network's department is required in order to access the network.
if(can_access_network(usr, get_camera_access(href_list["switch_network"])))
current_network = href_list["switch_network"]
else
to_chat(usr, "\The [nano_host()] shows an \"Network Access Denied\" error message.")
return 1
else if(href_list["reset"])
reset_current()
usr.reset_view(current_camera)
return 1
/datum/nano_module/camera_monitor/proc/switch_to_camera(var/mob/user, var/obj/machinery/camera/C)
//don't need to check if the camera works for AI because the AI jumps to the camera location and doesn't actually look through cameras.
if(isAI(user))
var/mob/living/silicon/ai/A = user
// Only allow non-carded AIs to view because the interaction with the eye gets all wonky otherwise.
if(!A.is_in_chassis())
return 0
A.eyeobj.setLoc(get_turf(C))
A.client.eye = A.eyeobj
return 1
set_current(C)
user.machine = nano_host()
user.reset_view(current_camera)
check_eye(user)
return 1
/datum/nano_module/camera_monitor/proc/set_current(var/obj/machinery/camera/C)
if(current_camera == C)
return
if(current_camera)
reset_current()
current_camera = C
if(current_camera)
var/mob/living/L = current_camera.loc
if(istype(L))
L.tracking_initiated()
/datum/nano_module/camera_monitor/proc/reset_current()
if(current_camera)
var/mob/living/L = current_camera.loc
if(istype(L))
L.tracking_cancelled()
current_camera = null
/datum/nano_module/camera_monitor/check_eye(var/mob/user as mob)
if(!current_camera)
return 0
var/viewflag = current_camera.check_eye(user)
if ( viewflag < 0 ) //camera doesn't work
reset_current()
return viewflag
// ERT Variant of the program
/datum/computer_file/program/camera_monitor/ert
filename = "ntcammon"
filedesc = "Advanced Camera Monitoring"
extended_desc = "This program allows remote access to station's camera system. Some camera networks may have additional access requirements. This version has an integrated database with additional encrypted keys."
size = 14
nanomodule_path = /datum/nano_module/camera_monitor/ert
available_on_ntnet = 0
/datum/nano_module/camera_monitor/ert
name = "Advanced Camera Monitoring Program"
available_to_ai = FALSE
// The ERT variant has access to ERT and crescent cams, but still checks for accesses. ERT members should be able to use it.
/datum/nano_module/camera_monitor/ert/modify_networks_list(var/list/networks)
..()
networks.Add(list(list("tag" = NETWORK_ERT, "has_access" = 1)))
networks.Add(list(list("tag" = NETWORK_CRESCENT, "has_access" = 1)))
return networks
@@ -1,23 +1,21 @@
/datum/computer_file/program/card_mod
filename = "cardmod"
filedesc = "ID card modification program"
nanomodule_path = /datum/nano_module/card_mod
nanomodule_path = /datum/nano_module/program/card_mod
program_icon_state = "id"
extended_desc = "Program for programming employee ID cards to access parts of the station."
required_access = access_change_ids
requires_ntnet = 0
size = 8
/datum/nano_module/card_mod
/datum/nano_module/program/card_mod
name = "ID card modification program"
var/mod_mode = 1
var/is_centcom = 0
var/show_assignments = 0
/datum/nano_module/card_mod/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
var/list/data = list()
if(program)
data = program.get_header_data()
/datum/nano_module/program/card_mod/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
var/list/data = host.initial_data()
data["src"] = "\ref[src]"
data["station_name"] = station_name()
@@ -89,7 +87,7 @@
ui.set_initial_data(data)
ui.open()
/datum/nano_module/card_mod/proc/format_jobs(list/jobs)
/datum/nano_module/program/card_mod/proc/format_jobs(list/jobs)
var/obj/item/weapon/card/id/id_card = program.computer.card_slot.stored_card
var/list/formatted = list()
for(var/job in jobs)
@@ -100,8 +98,7 @@
return formatted
/datum/nano_module/card_mod/proc/get_accesses(var/is_centcom = 0)
/datum/nano_module/program/card_mod/proc/get_accesses(var/is_centcom = 0)
return null
@@ -112,7 +109,7 @@
var/mob/user = usr
var/obj/item/weapon/card/id/user_id_card = user.GetIdCard()
var/obj/item/weapon/card/id/id_card = computer.card_slot.stored_card
var/datum/nano_module/card_mod/module = NM
var/datum/nano_module/program/card_mod/module = NM
switch(href_list["action"])
if("switchm")
if(href_list["target"] == "mod")
@@ -147,7 +144,7 @@
usr << "<span class='notice'>Hardware error: Printer was unable to print the file. It may be out of paper.</span>"
return
else
computer.visible_message("<span class='notice'>[computer] prints out paper.</span>")
computer.visible_message("<span class='notice'>\The [computer] prints out paper.</span>")
else
var/contents = {"<h4>Crew Manifest</h4>
<br>
@@ -157,7 +154,7 @@
usr << "<span class='notice'>Hardware error: Printer was unable to print the file. It may be out of paper.</span>"
return
else
computer.visible_message("<span class='notice'>[computer] prints out paper.</span>")
computer.visible_message("<span class='notice'>\The [computer] prints out paper.</span>")
if("eject")
if(computer && computer.card_slot)
if(id_card)
@@ -0,0 +1,312 @@
#define STATE_DEFAULT 1
#define STATE_MESSAGELIST 2
#define STATE_VIEWMESSAGE 3
#define STATE_STATUSDISPLAY 4
#define STATE_ALERT_LEVEL 5
/datum/computer_file/program/comm
filename = "comm"
filedesc = "Command and communications program."
program_icon_state = "comm"
nanomodule_path = /datum/nano_module/program/comm
extended_desc = "Used to command and control the station. Can relay long-range communications."
required_access = access_heads
requires_ntnet = 1
size = 12
usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP
network_destination = "station long-range communication array"
var/datum/comm_message_listener/message_core = new
/datum/computer_file/program/comm/clone()
var/datum/computer_file/program/comm/temp = ..()
temp.message_core.messages = null
temp.message_core.messages = message_core.messages.Copy()
return temp
/datum/nano_module/program/comm
name = "Command and communications program"
available_to_ai = TRUE
var/current_status = STATE_DEFAULT
var/msg_line1 = ""
var/msg_line2 = ""
var/centcomm_message_cooldown = 0
var/announcment_cooldown = 0
var/datum/announcement/priority/crew_announcement = new
var/current_viewing_message_id = 0
var/current_viewing_message = null
/datum/nano_module/program/comm/New()
..()
crew_announcement.newscast = 1
/datum/nano_module/program/comm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
var/list/data = host.initial_data()
if(program)
data["emagged"] = program.computer_emagged
data["net_comms"] = !!program.get_signal(NTNET_COMMUNICATION) //Double !! is needed to get 1 or 0 answer
data["net_syscont"] = !!program.get_signal(NTNET_SYSTEMCONTROL)
if(program.computer)
data["have_printer"] = !!program.computer.nano_printer
else
data["have_printer"] = 0
else
data["emagged"] = 0
data["net_comms"] = 1
data["net_syscont"] = 1
data["have_printer"] = 0
data["message_line1"] = msg_line1
data["message_line2"] = msg_line2
data["state"] = current_status
data["isAI"] = issilicon(usr)
data["authenticated"] = is_autenthicated(user)
data["boss_short"] = boss_short
data["current_security_level"] = security_level
data["current_security_level_title"] = num2seclevel(security_level)
data["def_SEC_LEVEL_DELTA"] = SEC_LEVEL_DELTA
data["def_SEC_LEVEL_BLUE"] = SEC_LEVEL_BLUE
data["def_SEC_LEVEL_GREEN"] = SEC_LEVEL_GREEN
var/datum/comm_message_listener/l = obtain_message_listener()
data["messages"] = l.messages
data["message_deletion_allowed"] = l != global_message_listener
data["message_current_id"] = current_viewing_message_id
if(current_viewing_message)
data["message_current"] = current_viewing_message
if(emergency_shuttle.location())
data["have_shuttle"] = 1
if(emergency_shuttle.online())
data["have_shuttle_called"] = 1
else
data["have_shuttle_called"] = 0
else
data["have_shuttle"] = 0
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui)
ui = new(user, src, ui_key, "communication.tmpl", name, 550, 420, state = state)
ui.auto_update_layout = 1
ui.set_initial_data(data)
ui.open()
/datum/nano_module/program/comm/proc/is_autenthicated(var/mob/user)
if(program)
return program.can_run(user)
return 1
/datum/nano_module/program/comm/proc/obtain_message_listener()
if(program)
var/datum/computer_file/program/comm/P = program
return P.message_core
return global_message_listener
/datum/nano_module/program/comm/Topic(href, href_list)
if(..())
return 1
var/mob/user = usr
var/ntn_comm = !!program.get_signal(NTNET_COMMUNICATION)
var/ntn_cont = !!program.get_signal(NTNET_SYSTEMCONTROL)
var/datum/comm_message_listener/l = obtain_message_listener()
switch(href_list["action"])
if("sw_menu")
current_status = text2num(href_list["target"])
if("announce")
if(is_autenthicated(user) && !issilicon(usr) && ntn_comm)
if(user)
var/obj/item/weapon/card/id/id_card = user.GetIdCard()
crew_announcement.announcer = GetNameAndAssignmentFromId(id_card)
else
crew_announcement.announcer = "Unknown"
if(announcment_cooldown)
usr << "Please allow at least one minute to pass between announcements"
nanomanager.update_uis(src)
return
var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") as null|text
if(!input || !can_still_topic())
nanomanager.update_uis(src)
return
crew_announcement.Announce(input)
announcment_cooldown = 1
spawn(600)//One minute cooldown
announcment_cooldown = 0
if("message")
if(href_list["target"] == "emagged")
if(program)
if(is_autenthicated(user) && program.computer_emagged && !issilicon(usr) && ntn_comm)
if(centcomm_message_cooldown)
usr << "<span class='warning'>Arrays recycling. Please stand by.</span>"
nanomanager.update_uis(src)
return
var/input = sanitize(input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", "") as null|text)
if(!input || !can_still_topic())
nanomanager.update_uis(src)
return
Syndicate_announce(input, usr)
usr << "<span class='notice'>Message transmitted.</span>"
log_say("[key_name(usr)] has made an illegal announcement: [input]")
centcomm_message_cooldown = 1
spawn(300)//30 second cooldown
centcomm_message_cooldown = 0
else if(href_list["target"] == "regular")
if(is_autenthicated(user) && !issilicon(usr) && ntn_comm)
if(centcomm_message_cooldown)
usr << "<span class='warning'>Arrays recycling. Please stand by.</span>"
nanomanager.update_uis(src)
return
if(!is_relay_online())//Contact Centcom has a check, Syndie doesn't to allow for Traitor funs.
usr <<"<span class='warning'>No Emergency Bluespace Relay detected. Unable to transmit message.</span>"
nanomanager.update_uis(src)
return
var/input = sanitize(input("Please choose a message to transmit to [boss_short] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", "") as null|text)
if(!input || !can_still_topic())
nanomanager.update_uis(src)
return
Centcomm_announce(input, usr)
usr << "<span class='notice'>Message transmitted.</span>"
log_say("[key_name(usr)] has made an IA [boss_short] announcement: [input]")
centcomm_message_cooldown = 1
spawn(300) //30 second cooldown
centcomm_message_cooldown = 0
if("shuttle")
if(is_autenthicated(user) && ntn_cont)
if(href_list["target"] == "call")
var/confirm = alert("Are you sure you want to call the shuttle?", name, "No", "Yes")
if(confirm == "Yes" && can_still_topic())
call_shuttle_proc(usr)
if(href_list["target"] == "cancel" && !issilicon(usr))
var/confirm = alert("Are you sure you want to cancel the shuttle?", name, "No", "Yes")
if(confirm == "Yes" && can_still_topic())
cancel_call_proc(usr)
if("setstatus")
if(is_autenthicated(user) && ntn_cont)
switch(href_list["target"])
if("line1")
var/linput = reject_bad_text(sanitize(input("Line 1", "Enter Message Text", msg_line1) as text|null, 40), 40)
if(can_still_topic())
msg_line1 = linput
if("line2")
var/linput = reject_bad_text(sanitize(input("Line 2", "Enter Message Text", msg_line2) as text|null, 40), 40)
if(can_still_topic())
msg_line2 = linput
if("message")
post_status("message", msg_line1, msg_line2)
if("alert")
post_status("alert", href_list["alert"])
else
post_status(href_list["target"])
if("setalert")
if(is_autenthicated(user) && !issilicon(usr) && ntn_cont && ntn_comm)
var/current_level = text2num(href_list["target"])
var/confirm = alert("Are you sure you want to change alert level to [num2seclevel(current_level)]?", name, "No", "Yes")
if(confirm == "Yes" && can_still_topic())
var/old_level = security_level
if(!current_level) current_level = SEC_LEVEL_GREEN
if(current_level < SEC_LEVEL_GREEN) current_level = SEC_LEVEL_GREEN
if(current_level > SEC_LEVEL_BLUE) current_level = SEC_LEVEL_BLUE //Cannot engage delta with this
set_security_level(current_level)
if(security_level != old_level)
log_game("[key_name(usr)] has changed the security level to [get_security_level()].")
message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].")
switch(security_level)
if(SEC_LEVEL_GREEN)
feedback_inc("alert_comms_green",1)
if(SEC_LEVEL_BLUE)
feedback_inc("alert_comms_blue",1)
else
usr << "You press button, but red light flashes and nothing happens." //This should never happen
current_status = STATE_DEFAULT
if("viewmessage")
if(is_autenthicated(user) && ntn_comm)
current_viewing_message_id = text2num(href_list["target"])
for(var/list/m in l.messages)
if(m["id"] == current_viewing_message_id)
current_viewing_message = m
current_status = STATE_VIEWMESSAGE
if("delmessage")
if(is_autenthicated(user) && ntn_comm && l != global_message_listener)
l.Remove(current_viewing_message)
current_status = STATE_MESSAGELIST
if("printmessage")
if(is_autenthicated(user) && ntn_comm)
if(program && program.computer && program.computer.nano_printer)
if(!program.computer.nano_printer.print_text(current_viewing_message["contents"],current_viewing_message["title"]))
usr << "<span class='notice'>Hardware error: Printer was unable to print the file. It may be out of paper.</span>"
else
program.computer.visible_message("<span class='notice'>\The [program.computer] prints out paper.</span>")
nanomanager.update_uis(src)
/datum/nano_module/program/comm/proc/post_status(var/command, var/data1, var/data2)
var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435)
if(!frequency) return
var/datum/signal/status_signal = new
status_signal.source = src
status_signal.transmission_method = 1
status_signal.data["command"] = command
switch(command)
if("message")
status_signal.data["msg1"] = data1
status_signal.data["msg2"] = data2
log_admin("STATUS: [key_name(usr)] set status screen message with [src]: [data1] [data2]")
if("alert")
status_signal.data["picture_state"] = data1
frequency.post_signal(src, status_signal)
#undef STATE_DEFAULT
#undef STATE_MESSAGELIST
#undef STATE_VIEWMESSAGE
#undef STATE_STATUSDISPLAY
#undef STATE_ALERT_LEVEL
/*
General message handling stuff
*/
var/list/comm_message_listeners = list() //We first have to initialize list then we can use it.
var/datum/comm_message_listener/global_message_listener = new //May be used by admins
var/last_message_id = 0
/proc/get_comm_message_id()
last_message_id = last_message_id + 1
return last_message_id
/proc/post_comm_message(var/message_title, var/message_text)
var/list/message = list()
message["id"] = get_comm_message_id()
message["title"] = message_title
message["contents"] = message_text
for (var/datum/comm_message_listener/l in comm_message_listeners)
l.Add(message)
//Old console support
for (var/obj/machinery/computer/communications/comm in machines)
if (!(comm.stat & (BROKEN | NOPOWER)) && comm.prints_intercept)
var/obj/item/weapon/paper/intercept = new /obj/item/weapon/paper( comm.loc )
intercept.name = message_title
intercept.info = message_text
comm.messagetitle.Add(message_title)
comm.messagetext.Add(message_text)
/datum/comm_message_listener
var/list/messages
/datum/comm_message_listener/New()
..()
messages = list()
comm_message_listeners.Add(src)
/datum/comm_message_listener/proc/Add(var/list/message)
messages[++messages.len] = message
/datum/comm_message_listener/proc/Remove(var/list/message)
messages -= list(message)
@@ -12,13 +12,13 @@
size = 4
available_on_ntnet = 0
requires_ntnet = 0
nanomodule_path = /datum/nano_module/computer_configurator/
nanomodule_path = /datum/nano_module/program/computer_configurator/
/datum/nano_module/computer_configurator
/datum/nano_module/program/computer_configurator
name = "NTOS Computer Configuration Tool"
var/obj/item/modular_computer/movable = null
/datum/nano_module/computer_configurator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
/datum/nano_module/program/computer_configurator/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)
movable = program.computer
if(!istype(movable))
@@ -1,3 +1,4 @@
#define MAX_TEXTFILE_LENGTH 128000 // 512GQ file
/datum/computer_file/program/filemanager
filename = "filemanager"
filedesc = "NTOS File Manager"
@@ -7,7 +8,7 @@
requires_ntnet = 0
available_on_ntnet = 0
undeletable = 1
nanomodule_path = /datum/nano_module/computer_filemanager/
nanomodule_path = /datum/nano_module/program/computer_filemanager/
var/open_file
var/error
@@ -83,8 +84,16 @@
var/datum/computer_file/data/F = HDD.find_file_by_name(open_file)
if(!F || !istype(F))
return 1
// 16384 is the limit for file length in characters. Currently, papers have value of 2048 so this is 8 times as long, since we can't edit parts of the file independently.
var/newtext = sanitize(html_decode(input(usr, "Editing file [open_file]. You may use most tags used in paper formatting:", "Text Editor", F.stored_data) as message), 16384)
if(F.do_not_edit && (alert("WARNING: This file is not compatible with editor. Editing it may result in permanently corrupted formatting or damaged data consistency. Edit anyway?", "Incompatible File", "No", "Yes") == "No"))
return 1
var/oldtext = html_decode(F.stored_data)
oldtext = replacetext(oldtext, "\[editorbr\]", "\n")
var/newtext = sanitize(replacetext(input(usr, "Editing file [open_file]. You may use most tags used in paper formatting:", "Text Editor", oldtext) as message|null, "\n", "\[editorbr\]"), MAX_TEXTFILE_LENGTH)
if(!newtext)
return
if(F)
var/datum/computer_file/data/backup = F.clone()
HDD.remove_file(F)
@@ -94,7 +103,7 @@
// This is mostly intended to prevent people from losing texts they spent lot of time working on due to running out of space.
// They will be able to copy-paste the text from error screen and store it in notepad or something.
if(!HDD.store_file(F))
error = "I/O error: Unable to overwrite file. Hard drive is probably full. You may want to backup your changes before closing this window:<br><br>[F.stored_data]<br><br>"
error = "I/O error: Unable to overwrite file. Hard drive is probably full. You may want to backup your changes before closing this window:<br><br>[html_decode(F.stored_data)]<br><br>"
HDD.store_file(backup)
if(href_list["PRG_printfile"])
. = 1
@@ -109,7 +118,7 @@
if(!computer.nano_printer)
error = "Missing Hardware: Your computer does not have required hardware to complete this operation."
return 1
if(!computer.nano_printer.print_text(parse_tags(F.stored_data)))
if(!computer.nano_printer.print_text(pencode2html(F.stored_data)))
error = "Hardware error: Printer was unable to print the file. It may be out of paper."
return 1
if(href_list["PRG_copytousb"])
@@ -137,56 +146,14 @@
if(.)
nanomanager.update_uis(NM)
/datum/computer_file/program/filemanager/proc/parse_tags(var/t)
t = replacetext(t, "\[center\]", "<center>")
t = replacetext(t, "\[/center\]", "</center>")
t = replacetext(t, "\[br\]", "<BR>")
t = replacetext(t, "\[b\]", "<B>")
t = replacetext(t, "\[/b\]", "</B>")
t = replacetext(t, "\[i\]", "<I>")
t = replacetext(t, "\[/i\]", "</I>")
t = replacetext(t, "\[u\]", "<U>")
t = replacetext(t, "\[/u\]", "</U>")
t = replacetext(t, "\[time\]", "[worldtime2text()]")
t = replacetext(t, "\[date\]", "[worlddate2text()]")
t = replacetext(t, "\[large\]", "<font size=\"4\">")
t = replacetext(t, "\[/large\]", "</font>")
t = replacetext(t, "\[h1\]", "<H1>")
t = replacetext(t, "\[/h1\]", "</H1>")
t = replacetext(t, "\[h2\]", "<H2>")
t = replacetext(t, "\[/h2\]", "</H2>")
t = replacetext(t, "\[h3\]", "<H3>")
t = replacetext(t, "\[/h3\]", "</H3>")
t = replacetext(t, "\[*\]", "<li>")
t = replacetext(t, "\[hr\]", "<HR>")
t = replacetext(t, "\[small\]", "<font size = \"1\">")
t = replacetext(t, "\[/small\]", "</font>")
t = replacetext(t, "\[list\]", "<ul>")
t = replacetext(t, "\[/list\]", "</ul>")
t = replacetext(t, "\[table\]", "<table border=1 cellspacing=0 cellpadding=3 style='border: 1px solid black;'>")
t = replacetext(t, "\[/table\]", "</td></tr></table>")
t = replacetext(t, "\[grid\]", "<table>")
t = replacetext(t, "\[/grid\]", "</td></tr></table>")
t = replacetext(t, "\[row\]", "</td><tr>")
t = replacetext(t, "\[tr\]", "</td><tr>")
t = replacetext(t, "\[td\]", "<td>")
t = replacetext(t, "\[cell\]", "<td>")
t = replacetext(t, "\[logo\]", "<img src = ntlogo.png>")
return t
/datum/nano_module/computer_filemanager
/datum/nano_module/program/computer_filemanager
name = "NTOS File Manager"
/datum/nano_module/computer_filemanager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
/datum/nano_module/program/computer_filemanager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
var/list/data = host.initial_data()
var/datum/computer_file/program/filemanager/PRG
var/list/data = list()
if(program)
data = program.get_header_data()
PRG = program
else
return
//var/list/data = program.get_header_data()
PRG = program
var/obj/item/weapon/computer_hardware/hard_drive/HDD
var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD
@@ -203,7 +170,7 @@
if(!istype(file))
data["error"] = "I/O ERROR: Unable to open file."
else
data["filedata"] = PRG.parse_tags(file.stored_data)
data["filedata"] = pencode2html(file.stored_data)
data["filename"] = "[file.filename].[file.filetype]"
else
if(!PRG.computer || !PRG.computer.hard_drive)
@@ -238,5 +205,4 @@
ui.auto_update_layout = 1
ui.set_initial_data(data)
ui.open()
#undef MAX_TEXTFILE_LENGTH
@@ -0,0 +1,151 @@
// This file is used as a reference for Modular Computers Development guide on the wiki. It contains a lot of excess comments, as it is intended as explanation
// for someone who may not be as experienced in coding. When making changes, please try to keep it this way.
// An actual program definition.
/datum/computer_file/program/game
filename = "arcadec" // File name, as shown in the file browser program.
filedesc = "Unknown Game" // User-Friendly name. In this case, we will generate a random name in constructor.
program_icon_state = "game" // Icon state of this program's screen.
extended_desc = "Fun for the whole family! Probably not an AAA title, but at least you can download it on the corporate network.." // A nice description.
size = 5 // Size in GQ. Integers only. Smaller sizes should be used for utility/low use programs (like this one), while large sizes are for important programs.
requires_ntnet = 0 // This particular program does not require NTNet network conectivity...
available_on_ntnet = 1 // ... but we want it to be available for download.
nanomodule_path = /datum/nano_module/arcade_classic/ // Path of relevant nano module. The nano module is defined further in the file.
var/picked_enemy_name
// Blatantly stolen and shortened version from arcade machines. Generates a random enemy name
/datum/computer_file/program/game/proc/random_enemy_name()
var/name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ", "the vibrating bluespace")
var/name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "Slime", "Lizard Man", "Unicorn", "Squirrel")
return "[name_part1] [name_part2]"
// When the program is first created, we generate a new enemy name and name ourselves accordingly.
/datum/computer_file/program/game/New()
..()
picked_enemy_name = random_enemy_name()
filedesc = "Defeat [picked_enemy_name]"
// Important in order to ensure that copied versions will have the same enemy name.
/datum/computer_file/program/game/clone()
var/datum/computer_file/program/game/G = ..()
G.picked_enemy_name = picked_enemy_name
return G
// When running the program, we also want to pass our enemy name to the nano module.
/datum/computer_file/program/game/run_program()
. = ..()
if(. && NM)
var/datum/nano_module/arcade_classic/NMC = NM
NMC.enemy_name = picked_enemy_name
// Nano module the program uses.
// This can be either /datum/nano_module/ or /datum/nano_module/program. The latter is intended for nano modules that are suposed to be exclusively used with modular computers,
// and should generally not be used, as such nano modules are hard to use on other places.
/datum/nano_module/arcade_classic/
name = "Classic Arcade"
var/player_mana // Various variables specific to the nano module. In this case, the nano module is a simple arcade game, so the variables store health and other stats.
var/player_health
var/enemy_mana
var/enemy_health
var/enemy_name = "Greytide Horde"
var/gameover
var/information
/datum/nano_module/arcade_classic/New()
..()
new_game()
// ui_interact handles transfer of data to NanoUI. Keep in mind that data you pass from here is actually sent to the client. In other words, don't send anything you don't want a client
// to see, and don't send unnecessarily large amounts of data (due to laginess).
/datum/nano_module/arcade_classic/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
var/list/data = host.initial_data()
data["player_health"] = player_health
data["player_mana"] = player_mana
data["enemy_health"] = enemy_health
data["enemy_mana"] = enemy_mana
data["enemy_name"] = enemy_name
data["gameover"] = gameover
data["information"] = information
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "arcade_classic.tmpl", "Defeat [enemy_name]", 500, 350, state = state)
if(host.update_layout())
ui.auto_update_layout = 1
ui.set_initial_data(data)
ui.open()
// Three helper procs i've created. These are unique to this particular nano module. If you are creating your own nano module, you'll most likely create similar procs too.
/datum/nano_module/arcade_classic/proc/enemy_play()
if((enemy_mana < 5) && prob(60))
var/steal = rand(2, 3)
player_mana -= steal
enemy_mana += steal
information += "[enemy_name] steals [steal] of your power!"
else if((enemy_health < 15) && (enemy_mana > 3) && prob(80))
var/healamt = min(rand(3, 5), enemy_mana)
enemy_mana -= healamt
enemy_health += healamt
information += "[enemy_name] heals for [healamt] health!"
else
var/dam = rand(3,6)
player_health -= dam
information += "[enemy_name] attacks for [dam] damage!"
/datum/nano_module/arcade_classic/proc/check_gameover()
if((player_health <= 0) || player_mana <= 0)
if(enemy_health <= 0)
information += "You have defeated [enemy_name], but you have died in the fight!"
else
information += "You have been defeated by [enemy_name]!"
gameover = 1
return TRUE
else if(enemy_health <= 0)
gameover = 1
information += "Congratulations! You have defeated [enemy_name]!"
return TRUE
return FALSE
/datum/nano_module/arcade_classic/proc/new_game()
player_mana = 10
player_health = 30
enemy_mana = 20
enemy_health = 45
gameover = FALSE
information = "A new game has started!"
/datum/nano_module/arcade_classic/Topic(href, href_list)
if(..()) // Always begin your Topic() calls with a parent call!
return 1
if(href_list["new_game"])
new_game()
return 1 // Returning 1 (TRUE) in Topic automatically handles UI updates.
if(gameover) // If the game has already ended, we don't want the following three topic calls to be processed at all.
return 1 // Instead of adding checks into each of those three, we can easily add this one check here to reduce on code copy-paste.
if(href_list["attack"])
var/damage = rand(2, 6)
information = "You attack for [damage] damage."
enemy_health -= damage
enemy_play()
check_gameover()
return 1
if(href_list["heal"])
var/healfor = rand(6, 8)
var/cost = rand(1, 3)
information = "You heal yourself for [healfor] damage, using [cost] energy in the process."
player_health += healfor
player_mana -= cost
enemy_play()
check_gameover()
return 1
if(href_list["regain_mana"])
var/regen = rand(4, 7)
information = "You rest of a while, regaining [regen] energy."
player_mana += regen
enemy_play()
check_gameover()
return 1
@@ -0,0 +1,129 @@
/datum/computer_file/program/newsbrowser
filename = "newsbrowser"
filedesc = "NTNet/ExoNet News Browser"
extended_desc = "This program may be used to view and download news articles from the network."
program_icon_state = "generic"
size = 8
requires_ntnet = 1
available_on_ntnet = 1
nanomodule_path = /datum/nano_module/program/computer_newsbrowser/
var/datum/computer_file/data/news_article/loaded_article
var/download_progress = 0
var/download_netspeed = 0
var/downloading = 0
var/message = ""
var/show_archived = 0
/datum/computer_file/program/newsbrowser/process_tick()
if(!downloading)
return
download_netspeed = 0
// Speed defines are found in misc.dm
switch(ntnet_status)
if(1)
download_netspeed = NTNETSPEED_LOWSIGNAL
if(2)
download_netspeed = NTNETSPEED_HIGHSIGNAL
if(3)
download_netspeed = NTNETSPEED_ETHERNET
download_progress += download_netspeed
if(download_progress >= loaded_article.size)
downloading = 0
requires_ntnet = 0 // Turn off NTNet requirement as we already loaded the file into local memory.
nanomanager.update_uis(NM)
/datum/computer_file/program/newsbrowser/kill_program()
..()
requires_ntnet = 1
loaded_article = null
download_progress = 0
downloading = 0
show_archived = 0
/datum/computer_file/program/newsbrowser/Topic(href, href_list)
if(..())
return 1
if(href_list["PRG_openarticle"])
. = 1
if(downloading || loaded_article)
return 1
for(var/datum/computer_file/data/news_article/N in ntnet_global.available_news)
if(N.uid == text2num(href_list["PRG_openarticle"]))
loaded_article = N.clone()
downloading = 1
break
if(href_list["PRG_reset"])
. = 1
downloading = 0
download_progress = 0
requires_ntnet = 1
loaded_article = null
if(href_list["PRG_clearmessage"])
. = 1
message = ""
if(href_list["PRG_savearticle"])
. = 1
if(downloading || !loaded_article)
return
var/savename = sanitize(input(usr, "Enter file name or leave blank to cancel:", "Save article", loaded_article.filename))
if(!savename)
return 1
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
if(!HDD)
return 1
var/datum/computer_file/data/news_article/N = loaded_article.clone()
N.filename = savename
HDD.store_file(N)
if(href_list["PRG_toggle_archived"])
. = 1
show_archived = !show_archived
if(.)
nanomanager.update_uis(NM)
/datum/nano_module/program/computer_newsbrowser
name = "NTNet/ExoNet News Browser"
/datum/nano_module/program/computer_newsbrowser/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
var/datum/computer_file/program/newsbrowser/PRG
var/list/data = list()
if(program)
data = program.get_header_data()
PRG = program
else
return
data["message"] = PRG.message
if(PRG.loaded_article && !PRG.downloading) // Viewing an article.
data["title"] = PRG.loaded_article.filename
data["article"] = PRG.loaded_article.stored_data
else if(PRG.downloading) // Downloading an article.
data["download_running"] = 1
data["download_progress"] = PRG.download_progress
data["download_maxprogress"] = PRG.loaded_article.size
data["download_rate"] = PRG.download_netspeed
else // Viewing list of articles
var/list/all_articles[0]
for(var/datum/computer_file/data/news_article/F in ntnet_global.available_news)
if(!PRG.show_archived && F.archived)
continue
all_articles.Add(list(list(
"name" = F.filename,
"size" = F.size,
"uid" = F.uid,
"archived" = F.archived
)))
data["all_articles"] = all_articles
data["showing_archived"] = PRG.show_archived
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "news_browser.tmpl", "NTNet/ExoNet News Browser", 575, 700, state = state)
ui.auto_update_layout = 1
ui.set_initial_data(data)
ui.open()
@@ -9,7 +9,7 @@
requires_ntnet = 1
requires_ntnet_feature = NTNET_SOFTWAREDOWNLOAD
available_on_ntnet = 0
nanomodule_path = /datum/nano_module/computer_ntnetdownload/
nanomodule_path = /datum/nano_module/program/computer_ntnetdownload/
ui_header = "downloader_finished.gif"
var/datum/computer_file/program/downloaded_file = null
var/hacked_download = 0
@@ -102,7 +102,7 @@
download_netspeed += delta
download_netspeed = round(download_netspeed, 0.002)//3 decimal places
var/delta_seconds = (download_last_update - world.time) * 10
var/delta_seconds = (world.time - download_last_update) / 10
download_completion = min(download_completion + delta_seconds * download_netspeed, downloaded_file.size)
@@ -130,11 +130,11 @@
return 1
return 0
/datum/nano_module/computer_ntnetdownload
/datum/nano_module/program/computer_ntnetdownload
name = "Network Downloader"
var/obj/item/modular_computer/my_computer = null
/datum/nano_module/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)
/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
@@ -164,7 +164,7 @@
var/list/all_entries[0]
for(var/datum/computer_file/program/P in ntnet_global.available_station_software)
// Only those programs our user can run will show in the list
if(!P.can_run(user))
if(!P.can_run(user) && P.requires_access_to_download)
continue
all_entries.Add(list(list(
"filename" = P.filename,
@@ -11,15 +11,12 @@
/datum/nano_module/computer_ntnetmonitor
name = "NTNet Diagnostics and Monitoring"
available_to_ai = TRUE
/datum/nano_module/computer_ntnetmonitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
if(!ntnet_global)
return
var/list/data = list()
if(program)
data = program.get_header_data()
var/list/data = host.initial_data()
data["ntnetstatus"] = ntnet_global.check_function()
data["ntnetrelays"] = ntnet_global.relays.len
@@ -38,7 +35,8 @@
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "ntnet_monitor.tmpl", "NTNet Diagnostics and Monitoring Tool", 575, 700, state = state)
ui.auto_update_layout = 1
if(host.update_layout())
ui.auto_update_layout = 1
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
@@ -9,7 +9,7 @@
network_destination = "NTNRC server"
ui_header = "ntnrc_idle.gif"
available_on_ntnet = 1
nanomodule_path = /datum/nano_module/computer_chatclient/
nanomodule_path = /datum/nano_module/program/computer_chatclient/
var/last_message = null // Used to generate the toolbar icon
var/username
var/datum/ntnet_conversation/channel = null
@@ -159,7 +159,7 @@
/datum/computer_file/program/chatclient/process_tick()
..()
if(running)
if(program_state != PROGRAM_STATE_KILLED)
ui_header = "ntnrc_idle.gif"
if(channel)
// Remember the last message. If there is no message in the channel remember null.
@@ -178,10 +178,10 @@
channel = null
..(forced)
/datum/nano_module/computer_chatclient
/datum/nano_module/program/computer_chatclient
name = "NTNet Relay Chat Client"
/datum/nano_module/computer_chatclient/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
/datum/nano_module/program/computer_chatclient/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
if(!ntnet_global || !ntnet_global.chat_channels)
return
@@ -10,7 +10,7 @@ var/global/nttransfer_uid = 0
requires_ntnet_feature = NTNET_PEERTOPEER
network_destination = "other device via P2P tunnel"
available_on_ntnet = 1
nanomodule_path = /datum/nano_module/computer_nttransfer/
nanomodule_path = /datum/nano_module/program/computer_nttransfer/
var/error = "" // Error screen
var/server_password = "" // Optional password to download the file.
@@ -87,13 +87,10 @@ var/global/nttransfer_uid = 0
download_completion = 0
/datum/nano_module/computer_nttransfer
/datum/nano_module/program/computer_nttransfer
name = "NTNet P2P Transfer Client"
/datum/nano_module/computer_nttransfer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
/datum/nano_module/program/computer_nttransfer/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)
return
var/datum/computer_file/program/nttransfer/PRG = program
@@ -0,0 +1,43 @@
// A wrapper that allows the computer to contain an intellicard.
/obj/item/weapon/computer_hardware/ai_slot
name = "intellicard slot"
desc = "An IIS interlink with connection uplinks that allow the device to interface with most common intellicard models. Too large to fit into tablets. Uses a lot of power when active."
icon_state = "aislot"
hardware_size = 2
critical = 0
power_usage = 100
origin_tech = list(TECH_POWER = 2, TECH_DATA = 3)
var/obj/item/weapon/aicard/stored_card
var/power_usage_idle = 100
var/power_usage_occupied = 2 KILOWATTS
/obj/item/weapon/computer_hardware/ai_slot/proc/update_power_usage()
if(!stored_card || !stored_card.carded_ai)
power_usage = power_usage_idle
return
power_usage = power_usage_occupied
/obj/item/weapon/computer_hardware/ai_slot/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
if(..())
return 1
if(istype(W, /obj/item/weapon/aicard))
if(stored_card)
user << "\The [src] is already occupied."
return
user.drop_from_inventory(W)
stored_card = W
W.forceMove(src)
update_power_usage()
if(istype(W, /obj/item/weapon/screwdriver))
user << "You manually remove \the [stored_card] from \the [src]."
stored_card.forceMove(get_turf(src))
stored_card = null
update_power_usage()
/obj/item/weapon/computer_hardware/ai_slot/Destroy()
if(holder2 && (holder2.ai_slot == src))
holder2.ai_slot = null
if(stored_card)
stored_card.forceMove(get_turf(holder2))
holder2 = null
..()
@@ -5,7 +5,8 @@
desc = "A standard power cell, commonly seen in high-end portable microcomputers or low-end laptops. It's rating is 750."
icon_state = "battery_normal"
critical = 1
malfunction_probability = 1
origin_tech = list(TECH_POWER = 1, TECH_ENGINEERING = 1)
var/battery_rating = 750
var/obj/item/weapon/cell/battery = null
@@ -13,6 +14,7 @@
name = "advanced battery"
desc = "An advanced power cell, often used in most laptops. It is too large to be fitted into smaller devices. It's rating is 1100."
icon_state = "battery_advanced"
origin_tech = list(TECH_POWER = 2, TECH_ENGINEERING = 2)
hardware_size = 2
battery_rating = 1100
@@ -20,6 +22,7 @@
name = "super battery"
desc = "A very advanced power cell, often used in high-end devices, or as uninterruptable power supply for important consoles or servers. It's rating is 1500."
icon_state = "battery_super"
origin_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 3)
hardware_size = 2
battery_rating = 1500
@@ -27,6 +30,7 @@
name = "ultra battery"
desc = "A very advanced large power cell. It's often used as uninterruptable power supply for critical consoles or servers. It's rating is 2000."
icon_state = "battery_ultra"
origin_tech = list(TECH_POWER = 5, TECH_ENGINEERING = 4)
hardware_size = 3
battery_rating = 2000
@@ -34,12 +38,14 @@
name = "micro battery"
desc = "A small power cell, commonly seen in most portable microcomputers. It's rating is 500."
icon_state = "battery_micro"
origin_tech = list(TECH_POWER = 2, TECH_ENGINEERING = 2)
battery_rating = 500
/obj/item/weapon/computer_hardware/battery_module/nano
name = "nano battery"
desc = "A tiny power cell, commonly seen in low-end portable microcomputers. It's rating is 300."
icon_state = "battery_nano"
origin_tech = list(TECH_POWER = 1, TECH_ENGINEERING = 1)
battery_rating = 300
// This is not intended to be obtainable in-game. Intended for adminbus and debugging purposes.
@@ -49,11 +55,20 @@
icon_state = "battery_lambda"
hardware_size = 1
battery_rating = 1000000
/obj/item/weapon/computer_hardware/battery_module/lambda/New()
..()
battery = new/obj/item/weapon/cell/infinite(src)
/obj/item/weapon/computer_hardware/battery_module/diagnostics(var/mob/user)
..()
user << "Internal battery charge: [battery.charge]/[battery.maxcharge] CU"
/obj/item/weapon/computer_hardware/battery_module/New()
battery = new/obj/item/weapon/cell(src)
battery.maxcharge = battery_rating
battery.charge = battery_rating
..()
battery.charge = 0
..()
/obj/item/weapon/computer_hardware/battery_module/proc/charge_to_full()
if(battery)
battery.charge = battery.maxcharge
@@ -5,6 +5,7 @@
critical = 0
icon_state = "cardreader"
hardware_size = 1
origin_tech = list(TECH_DATA = 2)
var/obj/item/weapon/card/id/stored_card = null
@@ -4,6 +4,7 @@
power_usage = 25 // SSD or something with low power usage
icon_state = "hdd_normal"
hardware_size = 1
origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1)
var/max_capacity = 128
var/used_capacity = 0
var/list/stored_files = list() // List of stored files on this drive. DO NOT MODIFY DIRECTLY!
@@ -12,6 +13,7 @@
name = "advanced hard drive"
desc = "A small hybrid hard drive with 256GQ of storage capacity for use in higher grade computers where balance between power efficiency and capacity is desired."
max_capacity = 256
origin_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 2)
power_usage = 50 // Hybrid, medium capacity and medium power storage
icon_state = "hdd_advanced"
hardware_size = 2
@@ -20,6 +22,7 @@
name = "super hard drive"
desc = "A small hard drive with 512GQ of storage capacity for use in cluster storage solutions where capacity is more important than power efficiency."
max_capacity = 512
origin_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 3)
power_usage = 100 // High-capacity but uses lots of power, shortening battery life. Best used with APC link.
icon_state = "hdd_super"
hardware_size = 2
@@ -28,6 +31,7 @@
name = "cluster hard drive"
desc = "A large storage cluster consisting of multiple hard drives for usage in high capacity storage systems. Has capacity of 2048 GQ."
power_usage = 500
origin_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 4)
max_capacity = 2048
icon_state = "hdd_cluster"
hardware_size = 3
@@ -37,6 +41,7 @@
name = "small hard drive"
desc = "A small highly efficient solid state drive for portable devices."
power_usage = 10
origin_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 2)
max_capacity = 64
icon_state = "hdd_small"
hardware_size = 1
@@ -45,10 +50,17 @@
name = "micro hard drive"
desc = "A small micro hard drive for portable devices."
power_usage = 2
origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1)
max_capacity = 32
icon_state = "hdd_micro"
hardware_size = 1
/obj/item/weapon/computer_hardware/hard_drive/diagnostics(var/mob/user)
..()
// 999 is a byond limit that is in place. It's unlikely someone will reach that many files anyway, since you would sooner run out of space.
user << "NT-NFS File Table Status: [stored_files.len]/999"
user << "Storage capacity: [used_capacity]/[max_capacity]GQ"
// Use this proc to add file to the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks.
/obj/item/weapon/computer_hardware/hard_drive/proc/store_file(var/datum/computer_file/F)
if(!F || !istype(F))
@@ -57,6 +69,9 @@
if(!can_store_file(F.size))
return 0
if(!check_functionality())
return 0
if(!stored_files)
return 0
@@ -84,6 +99,9 @@
if(!stored_files)
return 0
if(!check_functionality())
return 0
if(F in stored_files)
stored_files -= F
recalculate_size()
@@ -124,6 +142,9 @@
// Tries to find the file by filename. Returns null on failure
/obj/item/weapon/computer_hardware/hard_drive/proc/find_file_by_name(var/filename)
if(!check_functionality())
return null
if(!filename)
return null
@@ -1,14 +1,53 @@
/obj/item/weapon/computer_hardware/
name = "Hardware"
desc = "Unknown Hardware"
desc = "Unknown Hardware."
icon = 'icons/obj/modular_components.dmi'
var/obj/item/modular_computer/holder2 = null
var/power_usage = 0 // If the hardware uses extra power, change this.
var/enabled = 1 // If the hardware is turned off set this to 0.
var/critical = 1 // Prevent disabling for important component, like the HDD.
var/hardware_size = 1 // Limits which devices can contain this component. 1: Tablets/Laptops/Consoles, 2: Laptops/Consoles, 3: Consoles only
var/power_usage = 0 // If the hardware uses extra power, change this.
var/enabled = 1 // If the hardware is turned off set this to 0.
var/critical = 1 // Prevent disabling for important component, like the HDD.
var/hardware_size = 1 // Limits which devices can contain this component. 1: Tablets/Laptops/Consoles, 2: Laptops/Consoles, 3: Consoles only
var/damage = 0 // Current damage level
var/max_damage = 100 // Maximal damage level.
var/damage_malfunction = 20 // "Malfunction" threshold. When damage exceeds this value the hardware piece will semi-randomly fail and do !!FUN!! things
var/damage_failure = 50 // "Failure" threshold. When damage exceeds this value the hardware piece will not work at all.
var/malfunction_probability = 10// Chance of malfunction when the component is damaged
/obj/item/weapon/computer_hardware/attackby(var/obj/item/W as obj, var/mob/living/user as mob)
// Multitool. Runs diagnostics
if(istype(W, /obj/item/device/multitool))
user << "***** DIAGNOSTICS REPORT *****"
diagnostics(user)
user << "******************************"
return 1
// Nanopaste. Repair all damage if present for a single unit.
var/obj/item/stack/S = W
if(istype(S, /obj/item/stack/nanopaste))
if(!damage)
user << "\The [src] doesn't seem to require repairs."
return 1
if(S.use(1))
user << "You apply a bit of \the [W] to \the [src]. It immediately repairs all damage."
damage = 0
return 1
// Cable coil. Works as repair method, but will probably require multiple applications and more cable.
if(istype(S, /obj/item/stack/cable_coil))
if(!damage)
user << "\The [src] doesn't seem to require repairs."
return 1
if(S.use(1))
user << "You patch up \the [src] with a bit of \the [W]."
take_damage(-10)
return 1
return ..()
// Called on multitool click, prints diagnostic information to the user.
/obj/item/weapon/computer_hardware/proc/diagnostics(var/mob/user)
user << "Hardware Integrity Test... (Corruption: [damage]/[max_damage]) [damage > damage_failure ? "FAIL" : damage > damage_malfunction ? "WARN" : "PASS"]"
/obj/item/weapon/computer_hardware/New(var/obj/L)
w_class = hardware_size
if(istype(L, /obj/machinery/modular_computer))
var/obj/machinery/modular_computer/C = L
if(C.cpu)
@@ -20,4 +59,31 @@
/obj/item/weapon/computer_hardware/Destroy()
holder2 = null
..()
return ..()
// Handles damage checks
/obj/item/weapon/computer_hardware/proc/check_functionality()
// Too damaged to work at all.
if(damage > damage_failure)
return 0
// Still working. Well, sometimes...
if(damage > damage_malfunction)
if(prob(malfunction_probability))
return 0
// Good to go.
return 1
/obj/item/weapon/computer_hardware/examine(var/mob/user)
. = ..()
if(damage > damage_failure)
user << "<span class='danger'>It seems to be severely damaged!</span>"
else if(damage > damage_malfunction)
user << "<span class='notice'>It seems to be damaged!</span>"
else if(damage)
user << "It seems to be slightly damaged."
// Damages the component. Contains necessary checks. Negative damage "heals" the component.
/obj/item/weapon/computer_hardware/proc/take_damage(var/amount)
damage += round(amount) // We want nice rounded numbers here.
damage = between(0, damage, max_damage) // Clamp the value.
@@ -1,62 +1,46 @@
/obj/item/weapon/computer_hardware/nano_printer
name = "nano printer"
desc = "Small integrated printer with scanner and paper recycling module."
desc = "Small integrated printer with paper recycling module."
power_usage = 50
origin_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 2)
critical = 0
icon_state = "printer"
hardware_size = 1
var/stored_paper = 5
var/max_paper = 10
var/obj/item/weapon/paper/P = null // Currently stored paper for scanning.
/obj/item/weapon/computer_hardware/nano_printer/diagnostics(var/mob/user)
..()
user << "Paper buffer level: [stored_paper]/[max_paper]"
/obj/item/weapon/computer_hardware/nano_printer/proc/print_text(var/text_to_print, var/paper_title = null)
if(!stored_paper)
return 0
if(!enabled)
return 0
if(!check_functionality())
return 0
// Recycle stored paper
if(P)
stored_paper++
qdel(P)
P = null
// Damaged printer causes the resulting paper to be somewhat harder to read.
if(damage > damage_malfunction)
text_to_print = stars(text_to_print, 100-malfunction_probability)
new/obj/item/weapon/paper(get_turf(holder2),text_to_print, paper_title)
P = new/obj/item/weapon/paper(get_turf(holder2))
P.info = text_to_print
if(paper_title)
P.name = paper_title
P.update_icon()
stored_paper--
P = null
return 1
/obj/item/weapon/computer_hardware/nano_printer/proc/load_paper(var/obj/item/weapon/paper/paper)
if(!paper || !istype(paper))
return 0
/obj/item/weapon/computer_hardware/nano_printer/attackby(obj/item/W as obj, mob/user as mob)
if(istype(W, /obj/item/weapon/paper))
if(stored_paper >= max_paper)
user << "You try to add \the [W] into [src], but it's paper bin is full"
return
// We already have paper loaded, recycle it.
if(P && try_recycle_paper())
P = paper
P.forceMove(holder2)
/obj/item/weapon/computer_hardware/nano_printer/proc/try_recycle_paper()
if(!P)
return 0
if(stored_paper >= max_paper)
return 0
qdel(P)
P = null
return 1
user << "You insert \the [W] into [src]."
qdel(W)
stored_paper++
/obj/item/weapon/computer_hardware/nano_printer/Destroy()
if(holder2 && (holder2.nano_printer == src))
holder2.nano_printer = null
if(P)
if(holder2)
P.forceMove(get_turf(holder2))
else
qdel(P)
P = null
holder2 = null
..()
@@ -4,6 +4,7 @@ var/global/ntnet_card_uid = 1
name = "basic NTNet network card"
desc = "A basic network card for usage with standard NTNet frequencies."
power_usage = 50
origin_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 1)
critical = 0
icon_state = "netcard_basic"
hardware_size = 1
@@ -11,6 +12,18 @@ var/global/ntnet_card_uid = 1
var/identification_string = "" // Identification string, technically nickname seen in the network. Can be set by user.
var/long_range = 0
var/ethernet = 0 // Hard-wired, therefore always on, ignores NTNet wireless checks.
malfunction_probability = 1
/obj/item/weapon/computer_hardware/network_card/diagnostics(var/mob/user)
..()
user << "NIX Unique ID: [identification_id]"
user << "NIX User Tag: [identification_string]"
user << "Supported protocols:"
user << "511.m SFS (Subspace) - Standard Frequency Spread"
if(long_range)
user << "511.n WFS/HB (Subspace) - Wide Frequency Spread/High Bandiwdth"
if(ethernet)
user << "OpenEth (Physical Connection) - Physical network connection port"
/obj/item/weapon/computer_hardware/network_card/New(var/l)
..(l)
@@ -21,14 +34,16 @@ var/global/ntnet_card_uid = 1
name = "advanced NTNet network card"
desc = "An advanced network card for usage with standard NTNet frequencies. It's transmitter is strong enough to connect even off-station."
long_range = 1
origin_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 2)
power_usage = 100 // Better range but higher power usage.
icon_state = "netcard_advanced"
hardware_size = 1
/obj/item/weapon/computer_hardware/network_card/wired
name = "wired NTNet network card"
desc = "An advanced network card for usage with NTNet. This one uses wired connection."
desc = "An advanced network card for usage with standard NTNet frequencies. This one also supports wired connection."
ethernet = 1
origin_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 3)
power_usage = 100 // Better range but higher power usage.
icon_state = "netcard_ethernet"
hardware_size = 3
@@ -51,6 +66,9 @@ var/global/ntnet_card_uid = 1
if(!enabled)
return 0
if(!check_functionality())
return 0
if(ethernet) // Computer is connected via wired connection.
return 3
@@ -60,7 +78,11 @@ var/global/ntnet_card_uid = 1
if(holder2)
var/turf/T = get_turf(holder2)
if((T && istype(T)) && T.z in config.station_levels)
return 2
// Computer is on station. Low/High signal depending on what type of network card you have
if(long_range)
return 2
else
return 1
if(long_range) // Computer is not on station, but it has upgraded network card. Low signal.
return 1
@@ -1,29 +1,30 @@
// These are basically USB data sticks and may be used to transfer files between devices
/obj/item/weapon/computer_hardware/hard_drive/portable/
name = "basic data crystal"
desc = "Small crystal with imprinted photonic circuits that can be used to store data. It's capacity is 16 GQ"
desc = "Small crystal with imprinted photonic circuits that can be used to store data. Its capacity is 16 GQ."
power_usage = 10
icon_state = "flashdrive_basic"
hardware_size = 1
max_capacity = 16
origin_tech = list(TECH_DATA = 1)
/obj/item/weapon/computer_hardware/hard_drive/portable/advanced
name = "advanced data crystal"
desc = "Small crystal with imprinted high-density photonic circuits that can be used to store data. It's capacity is 64 GQ"
desc = "Small crystal with imprinted high-density photonic circuits that can be used to store data. Its capacity is 64 GQ."
power_usage = 20
icon_state = "flashdrive_advanced"
hardware_size = 1
max_capacity = 64
origin_tech = list(TECH_DATA = 2)
/obj/item/weapon/computer_hardware/hard_drive/portable/super
name = "super data crystal"
desc = "Small crystal with imprinted ultra-density photonic circuits that can be used to store data. It's capacity is 256 GQ"
desc = "Small crystal with imprinted ultra-density photonic circuits that can be used to store data. Its capacity is 256 GQ."
power_usage = 40
icon_state = "flashdrive_super"
hardware_size = 1
max_capacity = 256
origin_tech = list(TECH_DATA = 4)
/obj/item/weapon/computer_hardware/hard_drive/portable/New()
..()
@@ -8,6 +8,8 @@
hardware_size = 2
power_usage = 50
critical = 1
malfunction_probability = 1
origin_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 2)
var/max_idle_programs = 2 // 2 idle, + 1 active = 3 as said in description.
@@ -18,6 +20,7 @@
hardware_size = 1
power_usage = 25
max_idle_programs = 1
origin_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 2)
/obj/item/weapon/computer_hardware/processor_unit/photonic
name = "photonic processor"
@@ -26,6 +29,7 @@
hardware_size = 2
power_usage = 250
max_idle_programs = 4
origin_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 4)
/obj/item/weapon/computer_hardware/processor_unit/photonic/small
name = "photonic microprocessor"
@@ -33,4 +37,5 @@
icon_state = "cpu_small_photonic"
hardware_size = 1
power_usage = 75
max_idle_programs = 2
max_idle_programs = 2
origin_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 3)
@@ -2,9 +2,10 @@
name = "tesla link"
desc = "An advanced tesla link that wirelessly recharges connected device from nearby area power controller."
critical = 0
enabled = 0 // Starts turned off
enabled = 1
icon_state = "teslalink"
hardware_size = 2 // Can't be installed into tablets
origin_tech = list(TECH_DATA = 2, TECH_POWER = 3, TECH_ENGINEERING = 2)
var/obj/machinery/modular_computer/holder
/obj/item/weapon/computer_hardware/tesla_link/New(var/obj/L)
+51 -46
View File
@@ -2,7 +2,7 @@
/obj/machinery/lapvend
name = "computer vendor"
desc = "A vending machine with microfabricator capable of dispensing various NT-branded computers"
desc = "A vending machine with microfabricator capable of dispensing various NT-branded computers."
icon = 'icons/obj/vending.dmi'
icon_state = "robotics"
layer = 2.9
@@ -61,46 +61,46 @@
fabricated_laptop.cpu.processor_unit = new/obj/item/weapon/computer_hardware/processor_unit(fabricated_laptop.cpu)
total_price += 299
switch(dev_battery)
if(1) // Basic(750C)
if(1) //Micro(500C)
if(fabricate)
fabricated_laptop.cpu.battery_module = new/obj/item/weapon/computer_hardware/battery_module/micro(fabricated_laptop.cpu)
if(2) // Basic(750C)
if(fabricate)
fabricated_laptop.cpu.battery_module = new/obj/item/weapon/computer_hardware/battery_module(fabricated_laptop.cpu)
if(2) // Upgraded(1100C)
if(fabricate)
fabricated_laptop.cpu.battery_module = new/obj/item/weapon/computer_hardware/battery_module/advanced(fabricated_laptop.cpu)
total_price += 199
if(3) // Advanced(1500C)
if(fabricate)
fabricated_laptop.cpu.battery_module = new/obj/item/weapon/computer_hardware/battery_module/super(fabricated_laptop.cpu)
total_price += 499
// if(3) // Upgraded(1100C)
// if(fabricate)
// fabricated_laptop.cpu.battery_module = new/obj/item/weapon/computer_hardware/battery_module/advanced(fabricated_laptop.cpu)
// total_price += 499
switch(dev_disk)
if(1) // Basic(128GQ)
if(1)
if(fabricate)
fabricated_laptop.cpu.hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/small(fabricated_laptop.cpu)
if(2) // Basic(128GQ)
if(fabricate)
fabricated_laptop.cpu.hard_drive = new/obj/item/weapon/computer_hardware/hard_drive(fabricated_laptop.cpu)
if(2) // Upgraded(256GQ)
if(fabricate)
fabricated_laptop.cpu.hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/advanced(fabricated_laptop.cpu)
total_price += 99
if(3) // Advanced(512GQ)
if(fabricate)
fabricated_laptop.cpu.hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/super(fabricated_laptop.cpu)
total_price += 299
total_price += 199
// if(3) // Upgraded(256GQ)
// if(fabricate)
// fabricated_laptop.cpu.hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/advanced(fabricated_laptop.cpu)
// total_price += 299
switch(dev_netcard)
if(1) // Basic(Short-Range)
if(fabricate)
fabricated_laptop.cpu.network_card = new/obj/item/weapon/computer_hardware/network_card(fabricated_laptop.cpu)
total_price += 99
if(2) // Advanced (Long Range)
if(fabricate)
fabricated_laptop.cpu.network_card = new/obj/item/weapon/computer_hardware/network_card/advanced(fabricated_laptop.cpu)
total_price += 299
if(dev_tesla)
total_price += 399
if(fabricate)
fabricated_laptop.tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(fabricated_laptop)
if(dev_nanoprint)
total_price += 99
if(fabricate)
fabricated_laptop.cpu.nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(fabricated_laptop.cpu)
total_price += 199
// if(2) // Advanced (Long Range)
// if(fabricate)
// fabricated_laptop.cpu.network_card = new/obj/item/weapon/computer_hardware/network_card/advanced(fabricated_laptop.cpu)
// total_price += 299
// if(dev_tesla)
// total_price += 399
// if(fabricate)
// fabricated_laptop.tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(fabricated_laptop)
// if(dev_nanoprint)
// total_price += 99
// if(fabricate)
// fabricated_laptop.cpu.nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(fabricated_laptop.cpu)
if(dev_card)
total_price += 199
if(fabricate)
@@ -167,7 +167,8 @@
if(href_list["pick_device"])
if(state) // We've already picked a device type
return 0
devtype = text2num(href_list["pick_device"])
// devtype = text2num(href_list["pick_device"]) // Currently unavailable
devtype = 1
state = 1
fabricate_and_recalc_price(0)
return 1
@@ -197,12 +198,12 @@
fabricate_and_recalc_price(0)
return 1
if(href_list["hw_tesla"])
dev_tesla = text2num(href_list["hw_tesla"])
fabricate_and_recalc_price(0)
// dev_tesla = text2num(href_list["hw_tesla"]) // Currently unavailable
// fabricate_and_recalc_price(0)
return 1
if(href_list["hw_nanoprint"])
dev_nanoprint = text2num(href_list["hw_nanoprint"])
fabricate_and_recalc_price(0)
//dev_nanoprint = text2num(href_list["hw_nanoprint"]) // Currently unavailable
// fabricate_and_recalc_price(0)
return 1
if(href_list["hw_card"])
dev_card = text2num(href_list["hw_card"])
@@ -221,15 +222,17 @@
var/list/data[0]
data["state"] = state
data["devtype"] = devtype
data["hw_battery"] = dev_battery
data["hw_disk"] = dev_disk
data["hw_netcard"] = dev_netcard
data["hw_tesla"] = dev_tesla
data["hw_nanoprint"] = dev_nanoprint
data["hw_card"] = dev_card
data["hw_cpu"] = dev_cpu
data["totalprice"] = "[total_price]$"
if(state == 1)
data["devtype"] = devtype
data["hw_battery"] = dev_battery
data["hw_disk"] = dev_disk
data["hw_netcard"] = dev_netcard
data["hw_tesla"] = dev_tesla
data["hw_nanoprint"] = dev_nanoprint
data["hw_card"] = dev_card
data["hw_cpu"] = dev_cpu
if(state == 1 || state == 2)
data["totalprice"] = total_price
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
@@ -246,10 +249,12 @@ obj/machinery/lapvend/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(process_payment(I,W))
fabricate_and_recalc_price(1)
if((devtype == 1) && fabricated_laptop)
fabricated_laptop.cpu.battery_module.charge_to_full()
fabricated_laptop.forceMove(src.loc)
fabricated_laptop.close_laptop()
fabricated_laptop = null
else if((devtype == 2) && fabricated_tablet)
fabricated_tablet.battery_module.charge_to_full()
fabricated_tablet.forceMove(src.loc)
fabricated_tablet = null
ping("Enjoy your new product!")
@@ -291,4 +296,4 @@ obj/machinery/lapvend/attackby(obj/item/weapon/W as obj, mob/user as mob)
T.date = current_date_string
T.time = worldtime2text()
customer_account.transaction_log.Add(T)
return 1
return 1