diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm index 05aa4e419a2..47c36dc3f77 100644 --- a/code/_globalvars/lists/maintenance_loot.dm +++ b/code/_globalvars/lists/maintenance_loot.dm @@ -258,6 +258,8 @@ GLOBAL_LIST_INIT(uncommon_loot, list(//uncommon: useful items /obj/item/computer_disk/maintenance/scanner = 1, ///obj/item/computer_disk/maintenance/camera = 1, //SKYRAT EDIT REMOVAL - Available To Crew Now /obj/item/computer_disk/maintenance/modsuit_control = 1, + /obj/item/computer_disk/maintenance/spectre_meter = 1, + /obj/item/computer_disk/maintenance/arcade = 1, /obj/item/computer_disk/maintenance/theme = 3, ) = 3, //SKYRAT EDIT CHANGE - Original : 4 diff --git a/code/modules/cargo/markets/market_items/tools.dm b/code/modules/cargo/markets/market_items/tools.dm index 1be815e4f6b..c20834e640b 100644 --- a/code/modules/cargo/markets/market_items/tools.dm +++ b/code/modules/cargo/markets/market_items/tools.dm @@ -96,3 +96,12 @@ price_max = CARGO_CRATE_VALUE * 1.615 stock_max = 2 availability_prob = 50 + +/datum/market_item/tool/program_disk + name = "Bootleg Data Disk" + desc = "A data disk containing EXCLUSIVE and LIMITED modular programs. Legally, we're not allowed to tell you how we aquired them." + item = /obj/item/computer_disk/black_market + price_min = CARGO_CRATE_VALUE * 0.75 + price_max = CARGO_CRATE_VALUE * 2 + stock_max = 3 + availability_prob = 40 diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 2c94bf7fd48..420bfa74b54 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -640,9 +640,9 @@ return SSmodular_computers.add_log("[src]: [text]") /obj/item/modular_computer/proc/close_all_programs() - active_program = null + active_program?.kill_program() for(var/datum/computer_file/program/idle as anything in idle_threads) - idle_threads.Remove(idle) + idle.kill_program() /obj/item/modular_computer/proc/shutdown_computer(loud = TRUE) close_all_programs() diff --git a/code/modules/modular_computers/computers/item/disks/maintenance_disks.dm b/code/modules/modular_computers/computers/item/disks/maintenance_disks.dm index 1ef9fab39ce..430c101668d 100644 --- a/code/modules/modular_computers/computers/item/disks/maintenance_disks.dm +++ b/code/modules/modular_computers/computers/item/disks/maintenance_disks.dm @@ -14,6 +14,14 @@ /obj/item/computer_disk/maintenance/modsuit_control starting_programs = list(/datum/computer_file/program/maintenance/modsuit_control) +///Returns A 'spookiness' value based on the number of ghastly creature and hauntium and their distance from the PC. +/obj/item/computer_disk/maintenance/spectre_meter + starting_programs = list(/datum/computer_file/program/maintenance/spectre_meter) + +///A version of the arcade program with less HP/MP for the enemy and more for the player +/obj/item/computer_disk/maintenance/arcade + starting_programs = list(/datum/computer_file/program/arcade/eazy) + /obj/item/computer_disk/maintenance/theme/Initialize(mapload) starting_programs = list(pick(subtypesof(/datum/computer_file/program/maintenance/theme))) return ..() diff --git a/code/modules/modular_computers/computers/item/disks/unique_disks.dm b/code/modules/modular_computers/computers/item/disks/unique_disks.dm index 6d8614f56ac..1fd31957bef 100644 --- a/code/modules/modular_computers/computers/item/disks/unique_disks.dm +++ b/code/modules/modular_computers/computers/item/disks/unique_disks.dm @@ -9,3 +9,30 @@ /obj/item/computer_disk/syndicate/contractor starting_programs = list(/datum/computer_file/program/contract_uplink) + +/obj/item/computer_disk/black_market + desc = "Removable disk used to store data. This one has a smudged piece of paper glued to it, reading \"PC softwarez\"." + +/obj/item/computer_disk/black_market/Initialize(mapload) + icon_state = "datadisk[rand(0, 10)]" + //Populated with programs not found in the verified downloader app or that require access to download (but not to run). + var/list/potential_programs = list( + /datum/computer_file/program/arcade/eazy, + /datum/computer_file/program/radar/lifeline, + /datum/computer_file/program/radar/custodial_locator, + /datum/computer_file/program/supermatter_monitor, + /datum/computer_file/program/newscaster, + /datum/computer_file/program/secureye, + /datum/computer_file/program/crew_manifest, + /datum/computer_file/program/status, + ) + potential_programs += subtypesof(/datum/computer_file/program/maintenance) - /datum/computer_file/program/maintenance/theme + + var/total_programs_size = 0 + for(var/i in 1 to rand(2, 4)) + var/datum/computer_file/program/to_add = pick_n_take(potential_programs) + total_programs_size += initial(to_add.size) + starting_programs += to_add + ///Make sure the disk has enough space for all the programs + max_capacity = max(total_programs_size, max_capacity) + return ..() diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index c35d3dc5b17..5ddb064f319 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -187,8 +187,8 @@ if(src == computer.active_program) computer.active_program = null - if(computer.enabled) - computer.update_tablet_open_uis(user) + if(!QDELETED(computer) && computer.enabled) + INVOKE_ASYNC(computer, TYPE_PROC_REF(/obj/item/modular_computer, update_tablet_open_uis), user) if(src in computer.idle_threads) computer.idle_threads.Remove(src) @@ -211,5 +211,3 @@ computer.update_tablet_open_uis(usr) computer.update_appearance(UPDATE_ICON) return TRUE - -#undef PROGRAM_BASIC_CELL_USE diff --git a/code/modules/modular_computers/file_system/programs/arcade.dm b/code/modules/modular_computers/file_system/programs/arcade.dm index d4ced4c22ce..9f4db1963c5 100644 --- a/code/modules/modular_computers/file_system/programs/arcade.dm +++ b/code/modules/modular_computers/file_system/programs/arcade.dm @@ -23,6 +23,20 @@ ///Determines which boss image to use on the UI. var/boss_id = 1 +///Lazy version of the arade that can be found in maintenance disks +/datum/computer_file/program/arcade/eazy + filename = "dsarcadeez" + filedesc = "Donksoft Micro Arcade Ez" + filetype = "MNT" + program_flags = PROGRAM_UNIQUE_COPY + extended_desc = "Some sort of fan-made conversion of the classic game 'Outbomb Cuban Pete'. This one has you fight the weaker 'George Melon' instead." + boss_hp = 40 + boss_mp = 10 + player_hp = 35 + player_mp = 15 + heads_up = "Are you a bad enough dude to grief the station?" + boss_name = "George Melon" + /datum/computer_file/program/arcade/proc/game_check(mob/user) sleep(0.5 SECONDS) user?.mind?.adjust_experience(/datum/skill/gaming, 1) @@ -159,9 +173,9 @@ return TRUE if("Start_Game") game_active = TRUE - boss_hp = 45 - player_hp = 30 - player_mp = 10 + boss_hp = initial(boss_hp) + player_hp = initial(player_hp) + player_mp = initial(player_mp) heads_up = "You stand before [boss_name]! Prepare for battle!" program_open_overlay = "arcade" boss_id = rand(1,6) diff --git a/code/modules/modular_computers/file_system/programs/maintenance/camera.dm b/code/modules/modular_computers/file_system/programs/maintenance/camera.dm index be13d01ad27..c729e3ed5c4 100644 --- a/code/modules/modular_computers/file_system/programs/maintenance/camera.dm +++ b/code/modules/modular_computers/file_system/programs/maintenance/camera.dm @@ -33,6 +33,8 @@ if(internal_picture) QDEL_NULL(internal_picture) var/turf/our_turf = get_turf(tapped_atom) + var/spooky_camera = locate(/datum/computer_file/program/maintenance/spectre_meter) in computer.stored_files + internal_camera.see_ghosts = spooky_camera ? CAMERA_SEE_GHOSTS_BASIC : CAMERA_NO_GHOSTS internal_picture = internal_camera.captureimage(our_turf, user, internal_camera.picture_size_x + 1, internal_camera.picture_size_y + 1) picture_number++ computer.save_photo(internal_picture.picture_image) diff --git a/code/modules/modular_computers/file_system/programs/maintenance/modsuit.dm b/code/modules/modular_computers/file_system/programs/maintenance/modsuit.dm index 25baa43b8f5..c67b001942c 100644 --- a/code/modules/modular_computers/file_system/programs/maintenance/modsuit.dm +++ b/code/modules/modular_computers/file_system/programs/maintenance/modsuit.dm @@ -28,6 +28,7 @@ return TRUE /datum/computer_file/program/maintenance/modsuit_control/proc/unsync_modsuit(atom/source) + SIGNAL_HANDLER UnregisterSignal(controlled_suit, COMSIG_QDELETING) controlled_suit = null diff --git a/code/modules/modular_computers/file_system/programs/maintenance/spectre_meter.dm b/code/modules/modular_computers/file_system/programs/maintenance/spectre_meter.dm new file mode 100644 index 00000000000..8a3a3e6cb6e --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/maintenance/spectre_meter.dm @@ -0,0 +1,135 @@ + +#define SPOOK_VALUE_SAME_TURF_MULT 1.5 +#define SPOOK_VALUE_LIVING_MULT 6 +#define SPOOK_VALUE_DEF_MOB 10 +#define SPOOK_VALUE_ICON_STATE_MAX 120 +#define SPOOK_VALUE_SEGMENT 15 + +/datum/computer_file/program/maintenance/spectre_meter + filename = "spectre_meter" + filedesc = "Spectre-Meter" + power_cell_use = PROGRAM_BASIC_CELL_USE * 2 + downloader_category = PROGRAM_CATEGORY_EQUIPMENT + extended_desc = "A program used to somehow detect nearby spectral presence. Combine with the camera app to take photos of ghosts." + size = 7 + can_run_on_flags = PROGRAM_LAPTOP|PROGRAM_PDA + tgui_id = "NtosSpectreMeter" + program_icon = "ghost" + program_open_overlay = "spectre_meter_0" + /// The cooldown for manual scans + COOLDOWN_DECLARE(manual_scan_cd) + /// Whether the automatic scan mode is active or not + var/auto_mode = FALSE + /// The value reported by the last scan. + var/last_spook_value = 0 + var/datum/looping_sound/spectre_meter/soundloop + +/datum/computer_file/program/maintenance/spectre_meter/on_start(mob/user) + . = ..() + if(.) + soundloop = new() + +/datum/computer_file/program/maintenance/spectre_meter/kill_program() + QDEL_NULL(soundloop) + auto_mode = FALSE + last_spook_value = 0 + program_open_overlay = "spectre_meter_0" + power_cell_use = PROGRAM_BASIC_CELL_USE + STOP_PROCESSING(SSprocessing, src) + return ..() + +/datum/computer_file/program/maintenance/spectre_meter/ui_data(mob/user) + var/list/data = list() + data["spook_value"] = last_spook_value + data["auto_mode"] = auto_mode + data["on_cooldown"] = !COOLDOWN_FINISHED(src, manual_scan_cd) + return data + +/datum/computer_file/program/maintenance/spectre_meter/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + switch(action) + if("manual_scan") + if(COOLDOWN_FINISHED(src, manual_scan_cd)) + INVOKE_ASYNC(src, PROC_REF(scan_surroundings)) + COOLDOWN_START(src, manual_scan_cd, 2 SECONDS) + playsound(computer, 'sound/effects/ping_hit.ogg', vol = 40, vary = TRUE) + return TRUE + if("toggle_mode") + auto_mode = !auto_mode + if(auto_mode) + ///We want SSprocess. It fires twice as fast than the standard SSobjs used by [computer_file/program/process_tick] + START_PROCESSING(SSprocessing, src) + soundloop.start(computer) + else + STOP_PROCESSING(SSprocessing, src) + soundloop.stop(TRUE) + power_cell_use = auto_mode ? PROGRAM_BASIC_CELL_USE * 3 : PROGRAM_BASIC_CELL_USE + return TRUE + +/datum/computer_file/program/maintenance/spectre_meter/process(seconds_per_tick) + if(auto_mode) + INVOKE_ASYNC(src, PROC_REF(scan_surroundings)) + +///Return the "spook level" of the area the computer is in. +/datum/computer_file/program/maintenance/spectre_meter/proc/scan_surroundings() + var/spook_value = 0 + var/turf/turf = get_turf(computer) + + for(var/atom/atom as anything in range(5, turf)) + var/spook_amount = 0 + if(ismob(atom)) + ///ghastly mobs count toward spookiness more than observers. + var/spook_value_mult = 0 + if(isliving(atom)) + var/mob/living/living = atom + if(living.mob_biotypes & MOB_SPIRIT) + spook_value_mult = SPOOK_VALUE_LIVING_MULT + else if(isobserver(atom)) + spook_value_mult = 1 + spook_amount += SPOOK_VALUE_DEF_MOB * spook_value_mult + var/list/materials = atom.has_material_type(/datum/material/hauntium) + if(materials) + spook_amount += materials[/datum/material/hauntium]/SHEET_MATERIAL_AMOUNT + spook_amount += atom.reagents?.get_reagent_amount(/datum/reagent/hauntium)/20 + if(!spook_amount) + continue + if(atom.loc == turf) + spook_amount *= SPOOK_VALUE_SAME_TURF_MULT + spook_value += spook_amount/max(get_dist(turf, atom), 1) + CHECK_TICK + + soundloop.last_spook_value = last_spook_value = round(spook_value) + var/old_open_overlay = program_open_overlay + program_open_overlay = "spectre_meter_[min(FLOOR(last_spook_value, SPOOK_VALUE_SEGMENT), SPOOK_VALUE_ICON_STATE_MAX)]" + if(program_open_overlay != old_open_overlay) + computer.update_appearance(UPDATE_OVERLAYS) + +/datum/looping_sound/spectre_meter + mid_sounds = /datum/looping_sound/geiger::mid_sounds + mid_length = 2 + volume = 12 + var/last_spook_value = 0 + +/datum/looping_sound/spectre_meter/get_sound() + var/index = 1 + switch(last_spook_value) + if(0 to 14) + return null + if(14 to 40) + index = 1 + if(40 to 65) + index = 2 + if(65 to 90) + index = 3 + else + index = 4 + return ..(mid_sounds[index]) + +/datum/looping_sound/spectre_meter/stop(null_parent = FALSE) + last_spook_value = 0 + return ..() + +#undef SPOOK_VALUE_SAME_TURF_MULT +#undef SPOOK_VALUE_LIVING_MULT +#undef SPOOK_VALUE_DEF_MOB +#undef SPOOK_VALUE_ICON_STATE_MAX +#undef SPOOK_VALUE_SEGMENT diff --git a/icons/obj/modular_laptop.dmi b/icons/obj/modular_laptop.dmi index ee275066d16..c8ad438d1a3 100644 Binary files a/icons/obj/modular_laptop.dmi and b/icons/obj/modular_laptop.dmi differ diff --git a/icons/obj/modular_pda.dmi b/icons/obj/modular_pda.dmi index 6dfe29add9e..5a39c6cd726 100644 Binary files a/icons/obj/modular_pda.dmi and b/icons/obj/modular_pda.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 67848697858..ff13005272a 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -5138,6 +5138,7 @@ #include "code\modules\modular_computers\file_system\programs\maintenance\camera.dm" #include "code\modules\modular_computers\file_system\programs\maintenance\modsuit.dm" #include "code\modules\modular_computers\file_system\programs\maintenance\phys_scanner.dm" +#include "code\modules\modular_computers\file_system\programs\maintenance\spectre_meter.dm" #include "code\modules\modular_computers\file_system\programs\maintenance\themes.dm" #include "code\modules\modular_computers\file_system\programs\messenger\messenger_data.dm" #include "code\modules\modular_computers\file_system\programs\messenger\messenger_program.dm" diff --git a/tgui/packages/tgui/interfaces/NtosSpectreMeter.jsx b/tgui/packages/tgui/interfaces/NtosSpectreMeter.jsx new file mode 100644 index 00000000000..e52de5ac5b2 --- /dev/null +++ b/tgui/packages/tgui/interfaces/NtosSpectreMeter.jsx @@ -0,0 +1,56 @@ +import { useBackend } from '../backend'; +import { Box, Button, Icon, ProgressBar, Section } from '../components'; +import { NtosWindow } from '../layouts'; + +export const NtosSpectreMeter = (props) => { + const { act, data } = useBackend(); + const { auto_mode, spook_value, on_cooldown } = data; + return ( + + +
+ +
+
+
+ ); +};