The Spectre-Meter App, also a bootleg data disk item for the black market. (#80188)

## About The Pull Request
This PR adds in a new app that scans the nearby area for "spookiness"
(e.g. presence of ghosts, mobs with the spirit biotype, objects made
with hauntium or containing hauntium). A bit clunky by all means. It's a
maintenance app, and as such is more often found in the rare maintenance
computer disks, or downloadable from emagged PDAs (IIRC), or perhaps the
black market item which I've also added here as well, that might contain
it amongst other things.

Oh, if you also have the camera app, it'll let you take pictures of
ghosts like the 'camera obscura' does.

Oh, and there's also a maintenance version of the arcade program too;
just , like, lazier and easier.

## Why It's Good For The Game
Mostly a shower thought, 'cause I felt the idea maintenance disks to be
quite interesting yet lackluster, almost too niche. As for the remote
thought of using the app for validhunting, it isn't something you can
reliably get every and every other round, and if someone's got enough
ghosts circling them, chances are they're some big, loud antag or doing
something so cheeky, that they kinda deserve it.

Also, yeah, more black market stuff. Except for the misc section, it's
pretty lacking in uniqueness.

Screenshot of the UI, taken at the distance of one tile from a revenant:
![screenshot of the
UI](https://github.com/tgstation/tgstation/assets/42542238/402e2533-ae62-42c2-b90d-a3877940fb2c)

## Changelog

🆑
add: The Spectre-Meter modular computer app. A little, amatuerishly
coded app that, as the name implies, scan an area for spectral presence.
It can be found amongst other apps in maintenance computer disks.
add: An easier, lazier version of the Arcade app, also found in
maintenance.
add: Black market computer disks, which contains programs not readily
available to the average assistant.
/🆑

---------

Co-authored-by: Jacquerel <hnevard@gmail.com>
This commit is contained in:
Ghom
2023-12-18 18:32:00 +01:00
committed by GitHub
parent 1a0d888d73
commit 971bc2611b
15 changed files with 265 additions and 12 deletions
@@ -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)
@@ -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)
@@ -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
@@ -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