diff --git a/_maps/map_files/rift/rift-01-underground3.dmm b/_maps/map_files/rift/rift-01-underground3.dmm index c6410b6e242..5c1d19f00d7 100644 --- a/_maps/map_files/rift/rift-01-underground3.dmm +++ b/_maps/map_files/rift/rift-01-underground3.dmm @@ -585,6 +585,15 @@ }, /turf/simulated/floor/tiled/steel_grid, /area/quartermaster/mining_airlock) +"Hh" = ( +/obj/machinery/bioscan_antenna/permanent{ + network_key_obfuscated = "rift_bioscanners" + }, +/obj/machinery/bioscan_antenna/permanent{ + network_key_obfuscated = "rift_bioscanners" + }, +/turf/simulated/floor/plating, +/area/quartermaster/mining_airlock) "Ho" = ( /obj/effect/overlay/snow/floor/grav_surround{ dir = 1 @@ -20763,7 +20772,7 @@ KS KS aW QE -vr +Hh lP Tr pw diff --git a/_maps/map_files/rift/rift-02-underground2.dmm b/_maps/map_files/rift/rift-02-underground2.dmm index f6435408c55..cffb269e5b0 100644 --- a/_maps/map_files/rift/rift-02-underground2.dmm +++ b/_maps/map_files/rift/rift-02-underground2.dmm @@ -5536,6 +5536,9 @@ /obj/structure/railing{ dir = 4 }, +/obj/machinery/bioscan_antenna/permanent{ + network_key_obfuscated = "rift_bioscanners" + }, /turf/simulated/floor/plating, /area/maintenance/substation/atmos) "pW" = ( diff --git a/_maps/map_files/rift/rift-03-underground1.dmm b/_maps/map_files/rift/rift-03-underground1.dmm index c3155a968ee..a93642c4a25 100644 --- a/_maps/map_files/rift/rift-03-underground1.dmm +++ b/_maps/map_files/rift/rift-03-underground1.dmm @@ -3271,6 +3271,9 @@ /obj/effect/floor_decal/industrial/warning{ dir = 1 }, +/obj/machinery/bioscan_antenna/permanent{ + network_key_obfuscated = "rift_bioscanners" + }, /turf/simulated/floor/plating, /area/maintenance/public_bunker) "kt" = ( diff --git a/_maps/map_files/rift/rift-04-surface1.dmm b/_maps/map_files/rift/rift-04-surface1.dmm index e1122c66bb7..c08aaa61132 100644 --- a/_maps/map_files/rift/rift-04-surface1.dmm +++ b/_maps/map_files/rift/rift-04-surface1.dmm @@ -20923,6 +20923,9 @@ }, /obj/effect/floor_decal/industrial/warning, /obj/structure/railing, +/obj/machinery/bioscan_antenna/permanent{ + network_key_obfuscated = "rift_bioscanners" + }, /turf/simulated/floor/tiled/techfloor, /area/maintenance/security/lower) "nzE" = ( diff --git a/_maps/map_files/rift/rift-05-surface2.dmm b/_maps/map_files/rift/rift-05-surface2.dmm index b1f06b97807..b138e62d6c7 100644 --- a/_maps/map_files/rift/rift-05-surface2.dmm +++ b/_maps/map_files/rift/rift-05-surface2.dmm @@ -12417,6 +12417,9 @@ /obj/structure/cable/green{ icon_state = "2-4" }, +/obj/machinery/bioscan_antenna/permanent{ + network_key_obfuscated = "rift_bioscanners" + }, /turf/simulated/floor/plating, /area/maintenance/substation/surface_two) "jhU" = ( diff --git a/_maps/map_files/rift/rift-06-surface3.dmm b/_maps/map_files/rift/rift-06-surface3.dmm index 23e89879557..3edd5d3987a 100644 --- a/_maps/map_files/rift/rift-06-surface3.dmm +++ b/_maps/map_files/rift/rift-06-surface3.dmm @@ -7534,6 +7534,9 @@ /obj/structure/sign/deck3{ pixel_y = 32 }, +/obj/machinery/bioscan_antenna/permanent{ + network_key_obfuscated = "rift_bioscanners" + }, /turf/simulated/floor/tiled/techfloor, /area/rift/turbolift/maint) "aoO" = ( @@ -19897,6 +19900,9 @@ /obj/effect/floor_decal/corner/purple{ dir = 5 }, +/obj/machinery/computer/bioscan{ + network_key_obfuscated = "rift_bioscanners" + }, /turf/simulated/floor/tiled/dark, /area/bridge) "aOK" = ( diff --git a/_maps/map_files/rift/rift-07-west_base.dmm b/_maps/map_files/rift/rift-07-west_base.dmm index 1e0b9d09ac0..ebdc97cf71a 100644 --- a/_maps/map_files/rift/rift-07-west_base.dmm +++ b/_maps/map_files/rift/rift-07-west_base.dmm @@ -907,6 +907,9 @@ /obj/machinery/light/small{ dir = 8 }, +/obj/machinery/bioscan_antenna/permanent{ + network_key_obfuscated = "rift_bioscanners" + }, /turf/simulated/floor/tiled/steel_dirty/lythios43c/indoors, /area/rift/surfacebase/outside/west_base) "SE" = ( diff --git a/_maps/map_files/rift/rift-08-west_deep.dmm b/_maps/map_files/rift/rift-08-west_deep.dmm index 6f7e3810e07..19c0e5e9fc4 100644 --- a/_maps/map_files/rift/rift-08-west_deep.dmm +++ b/_maps/map_files/rift/rift-08-west_deep.dmm @@ -5664,6 +5664,9 @@ /obj/machinery/light/small{ dir = 8 }, +/obj/machinery/bioscan_antenna/permanent{ + network_key_obfuscated = "rift_bioscanners" + }, /turf/simulated/floor/plating, /area/outpost/mining_main/outpost/substation) diff --git a/_maps/map_files/rift/rift-09-west_caves.dmm b/_maps/map_files/rift/rift-09-west_caves.dmm index 87aa6909ed0..8778c828f93 100644 --- a/_maps/map_files/rift/rift-09-west_caves.dmm +++ b/_maps/map_files/rift/rift-09-west_caves.dmm @@ -916,6 +916,9 @@ /turf/simulated/floor/tiled/steel_dirty/lythios43c/indoors, /area/rift/facility/interior/medical) "kF" = ( +/obj/machinery/bioscan_antenna/permanent{ + network_key_obfuscated = "rift_bioscanners" + }, /turf/simulated/floor, /area/outpost/mining_main/outpost/substation) "kJ" = ( diff --git a/_maps/map_files/rift/rift-10-west_plains.dmm b/_maps/map_files/rift/rift-10-west_plains.dmm index 1f1b45c4d8e..4f9a873c1d2 100644 --- a/_maps/map_files/rift/rift-10-west_plains.dmm +++ b/_maps/map_files/rift/rift-10-west_plains.dmm @@ -6869,6 +6869,9 @@ /obj/structure/cable{ icon_state = "8-10" }, +/obj/machinery/bioscan_antenna/permanent{ + network_key_obfuscated = "rift_bioscanners" + }, /turf/simulated/floor, /area/outpost/mining_main/outpost/substation) "FP" = ( diff --git a/citadel.dme b/citadel.dme index e008aa2e2ec..424cf47fdcd 100644 --- a/citadel.dme +++ b/citadel.dme @@ -809,6 +809,7 @@ #include "code\game\atoms\atoms_vv.dm" #include "code\game\atoms\buckling.dm" #include "code\game\atoms\defense.dm" +#include "code\game\atoms\say.dm" #include "code\game\dna\dna2.dm" #include "code\game\dna\dna2_domutcheck.dm" #include "code\game\dna\dna2_helpers.dm" @@ -1112,6 +1113,7 @@ #include "code\game\machinery\computer\arcade\claw_machine.dm" #include "code\game\machinery\computer\arcade\orion.dm" #include "code\game\machinery\computer\arcade\orion_event.dm" +#include "code\game\machinery\computer\misc\bioscan.dm" #include "code\game\machinery\doors\airlock.dm" #include "code\game\machinery\doors\airlock_control.dm" #include "code\game\machinery\doors\airlock_electronics.dm" @@ -1139,6 +1141,7 @@ #include "code\game\machinery\embedded_controller\embedded_program_base.dm" #include "code\game\machinery\embedded_controller\mapping_helpers.dm" #include "code\game\machinery\embedded_controller\simple_docking_controller.dm" +#include "code\game\machinery\misc\bioscan_antenna.dm" #include "code\game\machinery\pipe\construction.dm" #include "code\game\machinery\pipe\pipe_dispenser.dm" #include "code\game\machinery\pipe\pipe_recipes.dm" @@ -1147,11 +1150,17 @@ #include "code\game\machinery\suit_storage\suit_storage_massive.dm" #include "code\game\machinery\suit_storage\suit_storage_prepared.dm" #include "code\game\machinery\suit_storage\suit_storage_unit.dm" +#include "code\game\machinery\telecomms\_telecomms.dm" +#include "code\game\machinery\telecomms\allinone.dm" #include "code\game\machinery\telecomms\broadcaster.dm" +#include "code\game\machinery\telecomms\bus.dm" +#include "code\game\machinery\telecomms\hub.dm" #include "code\game\machinery\telecomms\logbrowser.dm" -#include "code\game\machinery\telecomms\machine_interactions.dm" #include "code\game\machinery\telecomms\presets.dm" -#include "code\game\machinery\telecomms\telecomunications.dm" +#include "code\game\machinery\telecomms\processor.dm" +#include "code\game\machinery\telecomms\receiver.dm" +#include "code\game\machinery\telecomms\relay.dm" +#include "code\game\machinery\telecomms\server.dm" #include "code\game\machinery\telecomms\telemonitor.dm" #include "code\game\machinery\teleporter\console.dm" #include "code\game\machinery\teleporter\pad.dm" @@ -1358,19 +1367,23 @@ #include "code\game\objects\items\circuitboards\frame.dm" #include "code\game\objects\items\circuitboards\mecha.dm" #include "code\game\objects\items\circuitboards\other.dm" +#include "code\game\objects\items\circuitboards\computer\_computer.dm" #include "code\game\objects\items\circuitboards\computer\air_management.dm" #include "code\game\objects\items\circuitboards\computer\camera_monitor.dm" #include "code\game\objects\items\circuitboards\computer\computer.dm" +#include "code\game\objects\items\circuitboards\computer\misc.dm" #include "code\game\objects\items\circuitboards\computer\research.dm" #include "code\game\objects\items\circuitboards\computer\shuttle.dm" #include "code\game\objects\items\circuitboards\computer\supply.dm" #include "code\game\objects\items\circuitboards\computer\telecomms.dm" +#include "code\game\objects\items\circuitboards\machinery\_machine.dm" #include "code\game\objects\items\circuitboards\machinery\biogenerator.dm" #include "code\game\objects\items\circuitboards\machinery\cloning.dm" #include "code\game\objects\items\circuitboards\machinery\engineering.dm" #include "code\game\objects\items\circuitboards\machinery\jukebox.dm" #include "code\game\objects\items\circuitboards\machinery\mech_recharger.dm" #include "code\game\objects\items\circuitboards\machinery\mining_drill.dm" +#include "code\game\objects\items\circuitboards\machinery\misc.dm" #include "code\game\objects\items\circuitboards\machinery\pacman.dm" #include "code\game\objects\items\circuitboards\machinery\papershredder.dm" #include "code\game\objects\items\circuitboards\machinery\power.dm" @@ -3925,9 +3938,14 @@ #include "code\modules\research\designs\xenoarch_toys.dm" #include "code\modules\research\designs\xenobio_toys.dm" #include "code\modules\research\designs\circuits\ai_modules.dm" +#include "code\modules\research\designs\circuits\circuit.dm" #include "code\modules\research\designs\circuits\circuits.dm" #include "code\modules\research\designs\circuits\circuits_vr.dm" +#include "code\modules\research\designs\circuits\computer.dm" #include "code\modules\research\designs\circuits\disks.dm" +#include "code\modules\research\designs\circuits\machine.dm" +#include "code\modules\research\designs\circuits\computers\misc.dm" +#include "code\modules\research\designs\circuits\machines\misc.dm" #include "code\modules\resleeving\circuitboards.dm" #include "code\modules\resleeving\computers.dm" #include "code\modules\resleeving\documents.dm" diff --git a/code/__DEFINES/_cooldowns.dm b/code/__DEFINES/_cooldowns.dm index 1f844d0fbcf..e732f82371b 100644 --- a/code/__DEFINES/_cooldowns.dm +++ b/code/__DEFINES/_cooldowns.dm @@ -24,6 +24,7 @@ // INDEXES FOR TIMER COOLDOWNS - Must be unique! #define CD_INDEX_SONAR_PULSE "sonar_pulse" +#define CD_INDEX_SONAR_NOISE "sonar_noise" #define CD_INDEX_POWER_DRAIN_WARNING "power_drain_warning" #define CD_INDEX_TAPE_TRANSLATION "tape_translation" #define CD_INDEX_TAPE_PRINT "tape_print" diff --git a/code/__DEFINES/misc/message_ranges.dm b/code/__DEFINES/misc/message_ranges.dm index 321962729b3..434af54c848 100644 --- a/code/__DEFINES/misc/message_ranges.dm +++ b/code/__DEFINES/misc/message_ranges.dm @@ -4,3 +4,8 @@ #define MESSAGE_RANGE_COMBAT_SUBTLE 3 /// message range for loud combat actions #define MESSAGE_RANGE_COMBAT_LOUD 7 + +/// message range for most construction / deconstruction actions +#define MESSAGE_RANGE_CONSTRUCTION 5 +/// message range for most machine / structure configuration actions +#define MESSAGE_RANGE_CONFIGURATION 3 diff --git a/code/controllers/subsystem/mapping/obfuscation.dm b/code/controllers/subsystem/mapping/obfuscation.dm index 3cdea674efa..ab499d073f3 100644 --- a/code/controllers/subsystem/mapping/obfuscation.dm +++ b/code/controllers/subsystem/mapping/obfuscation.dm @@ -6,11 +6,14 @@ /datum/controller/subsystem/mapping /// "secret" key var/obfuscation_secret + /// subtly obfuscated id lookup + var/list/obfuscation_cache /datum/controller/subsystem/mapping/PreInit(recovering) . = ..() if(!obfuscation_secret) obfuscation_secret = md5(GUID()) + obfuscation_cache = recovering? ((istype(SSmapping) && SSmapping.obfuscation_cache) || list()) : list() /** * Generates an obfuscated but constant ID for an original ID for cases where you don't want players codediving for an ID. @@ -19,7 +22,20 @@ * * Both original and id_type are CASE INSENSITIVE. */ -/datum/controller/subsystem/mapping/proc/get_obfuscated_id(original, id_type = "GENERAL") +/datum/controller/subsystem/mapping/proc/get_obfuscated_id(original, id_type = "$any") if(!original) return // no. return md5("[obfuscation_secret]%[lowertext(original)]%[lowertext(id_type)]") + +/** + * more expensive obfuscation: just 4 random hexadecimals at the end. + * each given id should output the same resulting id. + * do not abuse this, we do cache this in memory. + * + * use in cases where you want a player-readable id that can be recovered + */ +/datum/controller/subsystem/mapping/proc/subtly_obfuscated_id(original, id_type = "$any") + if(isnull(obfuscation_cache[id_type]?[original])) + LAZYINITLIST(obfuscation_cache[id_type]) + obfuscation_cache[id_type][original] = "[original]_[num2text(rand(0, (16 ** 4) - 1), 4, 16)]" + return obfuscation_cache[id_type][original] diff --git a/code/game/atoms/atoms.dm b/code/game/atoms/atoms.dm index aa1e15b548b..163e05fe319 100644 --- a/code/game/atoms/atoms.dm +++ b/code/game/atoms/atoms.dm @@ -805,46 +805,6 @@ /atom/proc/GenerateTag() return -// todo: refactor this shit to be unified saycode, christ -/atom/proc/atom_say(message, datum/language/L) - if(!message) - return - var/list/speech_bubble_hearers = list() - var/no_runechat = FALSE - for(var/mob/M in get_hearers_in_view(MESSAGE_RANGE_COMBAT_LOUD, src)) - var/processed = message - if(L && !(L.name in M.languages)) - processed = L.scramble(message) - no_runechat = TRUE - M.show_message("[src] [L?.speech_verb || atom_say_verb], \"[processed]\"", 2, null, 1) - if(M.client) - speech_bubble_hearers += M.client - - if(length(speech_bubble_hearers) && !no_runechat) - var/image/I = generate_speech_bubble(src, "[bubble_icon][say_test(message)]", FLY_LAYER) - I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA - INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, speech_bubble_hearers, 30) - INVOKE_ASYNC(src, /atom/movable/proc/animate_chat, message, null, FALSE, speech_bubble_hearers, 30) - -/atom/proc/say_overhead(var/message, whispering, message_range = 7, var/datum/language/speaking = null, var/list/passed_hearing_list) - var/list/speech_bubble_hearers = list() - var/italics - if(whispering) - italics = TRUE - for(var/mob/M in get_mobs_in_view(message_range, src)) - if(M.client) - speech_bubble_hearers += M.client - if(length(speech_bubble_hearers)) - INVOKE_ASYNC(src, /atom/movable/proc/animate_chat, message, speaking, italics, speech_bubble_hearers, 30) - -/proc/generate_speech_bubble(var/bubble_loc, var/speech_state, var/set_layer = FLOAT_LAYER) - var/image/I = image('icons/mob/talk_vr.dmi', bubble_loc, speech_state, set_layer) - I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) - return I - -/atom/proc/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list()) - return - //! Radiation /** * called when we're hit by a radiation wave diff --git a/code/game/atoms/atoms_appearance.dm b/code/game/atoms/atoms_appearance.dm index a31524ce92d..71e9db05076 100644 --- a/code/game/atoms/atoms_appearance.dm +++ b/code/game/atoms/atoms_appearance.dm @@ -59,12 +59,21 @@ . |= SEND_SIGNAL(src, COMSIG_ATOM_UPDATED_ICON, updates, .) -/// Updates the icon state of the atom. +/** + * Updates the icon state of the atom. + * + * Make sure to call return ..() last, so the comsig can force updates. + */ /atom/proc/update_icon_state() SHOULD_CALL_PARENT(TRUE) return SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_ICON_STATE) -/// Updates the overlays of the atom. + +/** + * Updates the icon state of the atom. + * + * Make sure to call return ..() last, so the comsig can force updates. + */ /atom/proc/update_overlays() SHOULD_CALL_PARENT(TRUE) . = list() diff --git a/code/game/atoms/say.dm b/code/game/atoms/say.dm new file mode 100644 index 00000000000..c132226d563 --- /dev/null +++ b/code/game/atoms/say.dm @@ -0,0 +1,42 @@ +/** + * shim until saycode rewrite + */ +// todo: refactor this shit to be unified saycode, christ +/atom/proc/atom_say(message, datum/language/L) + if(!message) + return + var/list/speech_bubble_hearers = list() + var/no_runechat = FALSE + for(var/mob/M in get_hearers_in_view(MESSAGE_RANGE_COMBAT_LOUD, src)) + var/processed = message + if(L && !(L.name in M.languages)) + processed = L.scramble(message) + no_runechat = TRUE + M.show_message("[src] [L?.speech_verb || atom_say_verb], \"[processed]\"", 2, null, 1) + if(M.client) + speech_bubble_hearers += M.client + + if(length(speech_bubble_hearers) && !no_runechat) + var/image/I = generate_speech_bubble(src, "[bubble_icon][say_test(message)]", FLY_LAYER) + I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA + INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, speech_bubble_hearers, 30) + INVOKE_ASYNC(src, /atom/movable/proc/animate_chat, message, null, FALSE, speech_bubble_hearers, 30) + +/atom/proc/say_overhead(var/message, whispering, message_range = 7, var/datum/language/speaking = null, var/list/passed_hearing_list) + var/list/speech_bubble_hearers = list() + var/italics + if(whispering) + italics = TRUE + for(var/mob/M in get_mobs_in_view(message_range, src)) + if(M.client) + speech_bubble_hearers += M.client + if(length(speech_bubble_hearers)) + INVOKE_ASYNC(src, /atom/movable/proc/animate_chat, message, speaking, italics, speech_bubble_hearers, 30) + +/proc/generate_speech_bubble(var/bubble_loc, var/speech_state, var/set_layer = FLOAT_LAYER) + var/image/I = image('icons/mob/talk_vr.dmi', bubble_loc, speech_state, set_layer) + I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) + return I + +/atom/proc/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list()) + return diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index ac713a6382d..c63827c2a11 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -106,6 +106,15 @@ // todo: wow rad contents is a weird system rad_flags = RAD_BLOCK_CONTENTS + //* Construction / Deconstruction + /// Can be constructed / deconstructed by players by default + // todo: proc for allow / disallow, refactor + var/allow_deconstruct = FALSE + /// Can be anchored / unanchored by players without deconstructing by default + // todo: proc for allow / disallow, refactor, unify with can_be_unanchored + var/allow_unanchor = FALSE + + //* unsorted var/machine_stat = 0 var/emagged = FALSE /** diff --git a/code/game/machinery/computer/misc/bioscan.dm b/code/game/machinery/computer/misc/bioscan.dm new file mode 100644 index 00000000000..5cc4ccc4866 --- /dev/null +++ b/code/game/machinery/computer/misc/bioscan.dm @@ -0,0 +1,127 @@ +/obj/machinery/computer/bioscan + name = "Bioscan Control Console" + desc = "Used to pinpoint signatures, biological or otherwise, using a series of antennaes." + icon_keyboard = "tech_key" + + /// network key + var/network_key + /// automatically generate an obfuscated key - used by mappers + var/network_key_obfuscated + /// buffer - quite literally just holds a copy of generated ui data + var/list/buffer + /// last scan at + var/last_scan + /// scan cooldown + var/scan_delay = 10 SECONDS + +/obj/machinery/computer/bioscan/Initialize(mapload) + . = ..() + if(network_key_obfuscated) + network_key = SSmapping.subtly_obfuscated_id(network_key_obfuscated, "bioscan_network") + +/obj/machinery/computer/bioscan/ui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "BioscanConsole") + ui.open() + +/obj/machinery/computer/bioscan/ui_static_data(mob/user) + . = ..() + .["scan"] = buffer + .["antennas"] = ui_antenna_data() + +/obj/machinery/computer/bioscan/proc/ui_antenna_data() + var/list/antennas = network_key && GLOB.bioscan_antenna_list[network_key] + . = list() + for(var/obj/machinery/bioscan_antenna/A as anything in antennas) + var/turf/T = get_turf(A) + if(!T) + continue + . += list(list( + "level" = SSmapping.level_id(get_z(A)), + "id" = "[A.id]", + "anchor" = A.anchored, + "name" = A.name, + "x" = T.x, + "y" = T.y, + )) + +/obj/machinery/computer/bioscan/ui_data(mob/user, datum/tgui/ui, datum/ui_state/state) + . = ..() + .["scan_ready"] = !on_cooldown() + .["network"] = network_key || "" + +/obj/machinery/computer/bioscan/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + switch(action) + if("scan") + if(!network_key) + visible_message(SPAN_WARNING("[icon2html(src, world)] flashes a message, \"No network set.\"")) + return + if(on_cooldown()) + visible_message(SPAN_WARNING("[icon2html(src, world)] flashes a message, \"Bioscan still on cooldown.\"")) + return + last_scan = world.time + atom_say("Commencing signature scan and updating buffers...") + scan() + return TRUE + if("set_network") + var/what = params["network"] || null + set_network(what) + return TRUE + +/obj/machinery/computer/bioscan/proc/on_cooldown() + return (world.time - last_scan) <= scan_delay + +/obj/machinery/computer/bioscan/proc/set_network(key) + network_key = key + void_scan() + +/obj/machinery/computer/bioscan/proc/void_scan() + buffer = null + send_tgui_data_immediate(data = list("scan" = null)) + +/obj/machinery/computer/bioscan/proc/scan() + var/list/new_data = list() + /// get relevant antennas + var/list/antennas = network_key && GLOB.bioscan_antenna_list[network_key] + if(!length(antennas)) + // abort + void_scan() + return + /// build list of levels + var/list/indices = list() + for(var/obj/machinery/bioscan_antenna/A as anything in antennas) + indices["[A.z]"] = list() + /// get mobs + for(var/mob/M as anything in GLOB.mob_list) + if(!indices[num2text(M.z)]) + continue + indices[num2text(M.z)] += M + /// process mobs + var/list/assembled = list() + for(var/z_str in indices) + var/list/gottem = list() + gottem["id"] = SSmapping.level_id(text2num(z_str)) + var/mobs_all = 0 + var/mobs_complex = 0 + var/mobs_complex_alive = 0 + var/mobs_complex_dead = 0 + for(var/mob/M as anything in indices[z_str]) + ++mobs_all + if(!isliving(M)) + continue // don't care didn't ask + if(!issimple(M)) + ++mobs_complex + if(IS_DEAD(M)) + ++mobs_complex_dead + else + ++mobs_complex_alive + gottem["all"] = mobs_all + gottem["complex"] = mobs_complex + gottem["complex_alive"] = mobs_complex_alive + gottem["complex_dead"] = mobs_complex_dead + assembled[++assembled.len] = gottem + new_data["levels"] = assembled + buffer = new_data + send_tgui_data_immediate(data = list("scan" = buffer)) diff --git a/code/game/machinery/misc/bioscan_antenna.dm b/code/game/machinery/misc/bioscan_antenna.dm new file mode 100644 index 00000000000..4bde23f4809 --- /dev/null +++ b/code/game/machinery/misc/bioscan_antenna.dm @@ -0,0 +1,122 @@ +GLOBAL_LIST_EMPTY(bioscan_antenna_list) +/obj/machinery/bioscan_antenna + name = "Bioscan Antenna" + desc = "A fragile antenna used to locate nearby biosignatures." + allow_deconstruct = TRUE + allow_unanchor = TRUE + icon = 'icons/machinery/bioscan.dmi' + base_icon_state = "antenna" + icon_state = "antenna" + + /// network key + var/network_key + /// automatically generate an obfuscated key - used by mappers + var/network_key_obfuscated + /// can be reprogrammed + var/network_mutable = TRUE + /// id + var/id + /// next id + var/static/id_next = 0 + + // todo: scaling levels of how accurate they are + // todo: multiz / world-sector functionality; for now one must be there for each zlevel + +/obj/machinery/bioscan_antenna/Initialize(mapload) + . = ..() + id = "[++id_next]" + if(network_key_obfuscated) + network_key = SSmapping.subtly_obfuscated_id(network_key_obfuscated, "bioscan_network") + change_network(network_key) + +/obj/machinery/bioscan_antenna/Destroy() + change_network(null) + return ..() + +/obj/machinery/bioscan_antenna/crowbar_act(obj/item/I, mob/user, flags, hint) + if(!allow_deconstruct || !panel_open) + return ..() + if(default_deconstruction_crowbar(user, I)) + user.visible_message(SPAN_NOTICE("[user] dismantles [src]."), range = MESSAGE_RANGE_CONSTRUCTION) + return TRUE + return ..() + +/obj/machinery/bioscan_antenna/screwdriver_act(obj/item/I, mob/user, flags, hint) + if(!allow_deconstruct) + return ..() + if(default_deconstruction_screwdriver(user, I)) + user.visible_message(SPAN_NOTICE("[user] [panel_open? "opens" : "closes"] the panel on [src]."), range = MESSAGE_RANGE_CONSTRUCTION) + return TRUE + return ..() + +/obj/machinery/bioscan_antenna/wrench_act(obj/item/I, mob/user, flags, hint) + if(!allow_unanchor) + return ..() + if(default_unfasten_wrench(user, I, 4 SECONDS)) + user.visible_message(SPAN_NOTICE("[user] [anchored? "fastens [src] to the ground" : "unfastens [src] from the ground"]."), range = MESSAGE_RANGE_CONSTRUCTION) + return TRUE + return ..() + +/obj/machinery/bioscan_antenna/multitool_act(obj/item/I, mob/user, flags, hint) + if(!network_mutable) + return ..() + . = TRUE + var/new_network = default_input_text(user, "What do you want to set the network key to?", "Modify Network", network_key) + if(!user.Reachability(src) || isnull(new_network)) + return + user.visible_message(SPAN_NOTICE("[user] reprograms the network on [src]."), range = MESSAGE_RANGE_CONFIGURATION) + change_network(new_network) + +/obj/machinery/bioscan_antenna/dynamic_tool_functions(obj/item/I, mob/user) + . = list() + if(network_mutable) + .[TOOL_MULTITOOL] = "change network" + if(allow_unanchor) + .[TOOL_WRENCH] = anchored? "anchor" : "unanchor" + if(allow_deconstruct) + .[TOOL_SCREWDRIVER] = panel_open? "close panel" : "open panel" + if(panel_open) + .[TOOL_CROWBAR] = "deconstruct" + +/obj/machinery/bioscan_antenna/dynamic_tool_image(function, hint) + switch(function) + if(TOOL_WRENCH) + return anchored? dyntool_image_backward(TOOL_WRENCH) : dyntool_image_forward(TOOL_WRENCH) + if(TOOL_SCREWDRIVER) + return panel_open? dyntool_image_forward(TOOL_SCREWDRIVER) : dyntool_image_backward(TOOL_SCREWDRIVER) + return ..() + +/obj/machinery/bioscan_antenna/attack_hand(mob/user) + // todo: better xenomorphs + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(istype(H.species, /datum/species/xenos)) + if(!network_key) + to_chat(H, SPAN_WARNING("[src] is already de-programmed.")) + else + change_network(null) + H.visible_message(SPAN_WARNING("[H] violently tears apart [src]'s wires.")) + return + return ..() + +/obj/machinery/bioscan_antenna/proc/change_network(key) + if(src.network_key) + if(GLOB.bioscan_antenna_list[src.network_key]) + GLOB.bioscan_antenna_list[src.network_key] -= src + if(!length(GLOB.bioscan_antenna_list[src.network_key])) + GLOB.bioscan_antenna_list -= src.network_key + src.network_key = key + if(src.network_key) + if(!GLOB.bioscan_antenna_list[src.network_key]) + GLOB.bioscan_antenna_list[src.network_key] = list() + GLOB.bioscan_antenna_list[src.network_key] += src + update_icon() + +/obj/machinery/bioscan_antenna/update_icon_state() + icon_state = "[base_icon_state][network_key? "_active" : ""]" + return ..() + +/obj/machinery/bioscan_antenna/permanent + desc = "A less fragile antenna used to locate nearby biosignatures. This one cannot be anchored or moved, only reprogrammed." + allow_deconstruct = FALSE + allow_unanchor = FALSE diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/_telecomms.dm similarity index 57% rename from code/game/machinery/telecomms/machine_interactions.dm rename to code/game/machinery/telecomms/_telecomms.dm index 1b887cf1f2f..626f5cf9a50 100644 --- a/code/game/machinery/telecomms/machine_interactions.dm +++ b/code/game/machinery/telecomms/_telecomms.dm @@ -1,5 +1,271 @@ //This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 +/* + Hello, friends, this is Doohl from sexylands. You may be wondering what this + monstrous code file is. Sit down, boys and girls, while I tell you the tale. + + + The machines defined in this file were designed to be compatible with any radio + signals, provided they use subspace transmission. Currently they are only used for + headsets, but they can eventually be outfitted for real COMPUTER networks. This + is just a skeleton, ladies and gentlemen. + + Look at radio.dm for the prequel to this code. +*/ + +/obj/machinery/telecomms + icon = 'icons/obj/stationobjs.dmi' + ///List of machines this machine is linked to. + var/list/links = list() + ///Value increases as traffic increases. + var/traffic = 0 + ///How much traffic to lose per tick (50 gigabytes/second * netspeed). + var/netspeed = 5 + ///List of text/number values to link with. + var/list/autolinkers = list() + ///Identification string. + var/id = "NULL" + ///The network of the machinery. + var/network = "NULL" + //List of frequencies to tune into: if none, will listen to all. + var/list/freq_listening = list() + ///Just a hacky way of preventing alike machines from pairing. + var/machinetype = 0 + ///Is it toggled on. //Not sure why we have this when we have var/on. + var/toggled = TRUE + var/on = TRUE + ///Basically HP, loses integrity by heat. + integrity = 100 + ///Whether the machine will produce heat when on. + var/produces_heat = TRUE + ///How many process() ticks to delay per heat. + var/delay = 10 + ///Can you link it across Z levels or on the otherside of the map? (Relay & Hub) + var/long_range_link = FALSE + ///Is it a hidden machine? + var/hide = 0 + ///0 = auto set in New() - this is the z level that the machine is listening to. + var/listening_level = 0 + + +/obj/machinery/telecomms/proc/relay_information(datum/signal/signal, filter, copysig, amount = 20) + // relay signal to all linked machinery that are of type [filter]. If signal has been sent [amount] times, stop sending + + if(!on) + return + //TO_WORLD("[src] ([src.id]) - [signal.debug_print()]") + var/send_count = 0 + + signal.data["slow"] += rand(0, round((100-integrity))) // apply some lag based on integrity + + /* + // Edit by Atlantis: Commented out as emergency fix due to causing extreme delays in communications. + // Apply some lag based on traffic rates + var/netlag = round(traffic / 50) + if(netlag > signal.data["slow"]) + signal.data["slow"] = netlag + */ +// Loop through all linked machines and send the signal or copy. + for(var/obj/machinery/telecomms/machine in links) + if(filter && !istype( machine, text2path(filter) )) + continue + if(!machine.on) + continue + if(amount && send_count >= amount) + break + if(machine.loc.z != listening_level) + if(long_range_link == 0 && machine.long_range_link == 0) + continue + // If we're sending a copy, be sure to create the copy for EACH machine and paste the data + var/datum/signal/copy + if(copysig) + copy = new + copy.transmission_method = TRANSMISSION_SUBSPACE + copy.frequency = signal.frequency + copy.data = signal.data.Copy() + + // Keep the "original" signal constant + if(!signal.data["original"]) + copy.data["original"] = signal + else + copy.data["original"] = signal.data["original"] + + send_count++ + if(machine.is_freq_listening(signal)) + machine.traffic++ + + if(copysig && copy) + machine.receive_information(copy, src) + else + machine.receive_information(signal, src) + + + if(send_count > 0 && is_freq_listening(signal)) + traffic++ + + return send_count + +/obj/machinery/telecomms/proc/relay_direct_information(datum/signal/signal, obj/machinery/telecomms/machine) + // send signal directly to a machine + machine.receive_information(signal, src) + +/obj/machinery/telecomms/proc/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + // receive information from linked machinery + +/obj/machinery/telecomms/proc/is_freq_listening(datum/signal/signal) + // return 1 if found, 0 if not found + if(!signal) + return 0 + if((signal.frequency in freq_listening) || (!freq_listening.len)) + return 1 + else + return 0 + +/obj/machinery/telecomms/Initialize(mapload) + GLOB.telecomms_list += src + . = ..() + + //Set the listening_level if there's none. + if(!listening_level) + //Defaults to our Z level! + var/turf/position = get_turf(src) + listening_level = position.z + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/telecomms/LateInitialize() + if(autolinkers.len) + // Links nearby machines + if(!long_range_link) + for(var/obj/machinery/telecomms/T in orange(20, src)) + add_link(T) + else + for(var/obj/machinery/telecomms/T in GLOB.telecomms_list) + add_link(T) + return ..() + +/obj/machinery/telecomms/Destroy() + GLOB.telecomms_list -= src + for(var/obj/machinery/telecomms/comm in GLOB.telecomms_list) + comm.links -= src + links = list() + ..() + +// Used in auto linking +/obj/machinery/telecomms/proc/add_link(var/obj/machinery/telecomms/T) + var/pos_z = get_z(src) + var/tpos_z = get_z(T) + if((pos_z == tpos_z) || (src.long_range_link && T.long_range_link)) + for(var/x in autolinkers) + if(T.autolinkers.Find(x)) + if(src != T) + links |= T + +/obj/machinery/telecomms/update_icon() + if(on) + icon_state = initial(icon_state) + else + icon_state = "[initial(icon_state)]_off" + +/obj/machinery/telecomms/proc/update_power() + if(toggled) + if(machine_stat & (BROKEN|NOPOWER|EMPED) || integrity <= 0) // if powered, on. if not powered, off. if too damaged, off + on = FALSE + else + on = TRUE + else + on = FALSE + +/obj/machinery/telecomms/process() + update_power() + + // Check heat and generate some + checkheat() + + // Update the icon + update_icon() + + if(traffic > 0) + traffic -= netspeed + +/obj/machinery/telecomms/emp_act(severity) + if(prob(100/severity)) + if(!(machine_stat & EMPED)) + machine_stat |= EMPED + var/duration = (300 * 10)/severity + spawn(rand(duration - 20, duration + 20)) // Takes a long time for the machines to reboot. + machine_stat &= ~EMPED + ..() + +/obj/machinery/telecomms/proc/checkheat() + // Checks heat from the environment and applies any integrity damage + var/damage_chance = 0 // Percent based chance of applying 1 integrity damage this tick + switch(loc.return_temperature()) + if((T0C + 40) to (T0C + 70)) // 40C-70C, minor overheat, 10% chance of taking damage + damage_chance = 10 + if((T0C + 70) to (T0C + 130)) // 70C-130C, major overheat, 25% chance of taking damage + damage_chance = 25 + if((T0C + 130) to (T0C + 200)) // 130C-200C, dangerous overheat, 50% chance of taking damage + damage_chance = 50 + if((T0C + 200) to INFINITY) // More than 200C, INFERNO. Takes damage every tick. + damage_chance = 100 + if (damage_chance && prob(damage_chance)) + integrity = clamp( integrity - 1, 0, 100) + + if(delay > 0) + delay-- + else if(on) + produce_heat() + delay = initial(delay) + +/obj/machinery/telecomms/proc/produce_heat() + if (!produces_heat) + return + + if (!use_power) + return + + if(!(machine_stat & (NOPOWER|BROKEN))) + var/turf/simulated/L = loc + if(istype(L)) + var/datum/gas_mixture/env = L.return_air() + + var/transfer_moles = 0.25 * env.total_moles + + var/datum/gas_mixture/removed = env.remove(transfer_moles) + + if(removed) + + var/heat_produced = idle_power_usage //obviously can't produce more heat than the machine draws from it's power source + if (traffic <= 0) + heat_produced *= 0.30 //if idle, produce less heat. + + removed.adjust_thermal_energy(heat_produced) + + env.merge(removed) + +//Generic telecomm connectivity test proc +/proc/can_telecomm(var/atom/A, var/atom/B, var/ad_hoc = FALSE) + if(!A || !B) + log_debug(SPAN_DEBUG("can_telecomm(): Undefined endpoints!")) + return FALSE + + //Can't in this case, obviously! + if(is_jammed(A) || is_jammed(B)) + return FALSE + + //Items don't have a Z when inside an object or mob + var/turf/src_z = get_z(A) + var/turf/dst_z = get_z(B) + + //Nullspace, probably. + if(!src_z || !dst_z) + return FALSE + + //We can do the simple check first, if you have ad_hoc radios. + if(ad_hoc && src_z == dst_z) + return TRUE + + return src_z in GLOB.using_map.get_map_levels(dst_z, TRUE, om_range = DEFAULT_OVERMAP_RANGE) /* diff --git a/code/game/machinery/telecomms/allinone.dm b/code/game/machinery/telecomms/allinone.dm new file mode 100644 index 00000000000..23f48b5d8c4 --- /dev/null +++ b/code/game/machinery/telecomms/allinone.dm @@ -0,0 +1,200 @@ +/* + Basically just an empty shell for receiving and broadcasting radio messages. Not + very flexible, but it gets the job done. + NOTE: This AIO device listens on *every* zlevel (it does not even check) +*/ + +/obj/machinery/telecomms/allinone + name = "Telecommunications Mainframe" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "allinone" + desc = "A compact machine used for portable subspace telecommuniations processing." + density = 1 + use_power = USE_POWER_IDLE + idle_power_usage = 20 + anchored = 1 + machinetype = 6 + produces_heat = 0 + var/intercept = 0 // if nonzero, broadcasts all messages to syndicate channel + var/overmap_range = 0 //Same turf + + var/list/linked_radios_weakrefs = list() + +/obj/machinery/telecomms/allinone/proc/link_radio(var/obj/item/radio/R) + if(!istype(R)) + return + linked_radios_weakrefs |= WEAKREF(R) + +/obj/machinery/telecomms/allinone/receive_signal(datum/signal/signal) + + // Has to be on to receive messages + if(!on) + return + + // Why did you use this subtype? + if(!GLOB.using_map.use_overmap) + return + + // Someone else handling it? + if(signal.data["done"]) + return + + // Where are we able to hear from (and talk to, since we're AIO) anyway? + var/map_levels = GLOB.using_map.get_map_levels(z, TRUE, overmap_range) + + //Bluespace can skip this check + if(signal.transmission_method != TRANSMISSION_BLUESPACE) + var/list/signal_levels = list() + signal_levels += signal.data["level"] //If it's text/number, it'll be the only entry, if it's a list, it'll get combined + var/list/overlap = map_levels & signal_levels //Returns a list of similar levels + if(!overlap.len) + return + + if(is_freq_listening(signal)) // detect subspace signals + + signal.data["done"] = 1 // mark the signal as being broadcasted since we're a broadcaster + signal.data["compression"] = 0 // decompress since we're a processor + + // Search for the original signal and mark it as done as well + var/datum/signal/original = signal.data["original"] + if(original) + original.data["done"] = 1 + + // For some reason level is both used as a list and not a list, and now it needs to be a list. + signal.data["level"] = map_levels + + if(signal.data["slow"] > 0) + sleep(signal.data["slow"]) // simulate the network lag if necessary + + /* ###### Broadcast a message using signal.data ###### */ + + var/datum/radio_frequency/connection = signal.data["connection"] + + var/list/forced_radios + for(var/datum/weakref/wr in linked_radios_weakrefs) + var/obj/item/radio/R = wr.resolve() + if(istype(R)) + LAZYDISTINCTADD(forced_radios, R) + + Broadcast_Message( + signal.data["connection"], + signal.data["mob"], + signal.data["vmask"], + signal.data["vmessage"], + signal.data["radio"], + signal.data["message"], + signal.data["name"], + signal.data["job"], + signal.data["realname"], + signal.data["vname"], + DATA_NORMAL, + signal.data["compression"], + signal.data["level"], + connection.frequency, + signal.data["verb"], + signal.data["language"], + forced_radios + ) + +//Antag version with unlimited range (doesn't even check) and uses no power, to enable antag comms to work anywhere. +/obj/machinery/telecomms/allinone/antag + use_power = USE_POWER_OFF + idle_power_usage = 0 + +/obj/machinery/telecomms/allinone/antag/receive_signal(datum/signal/signal) + if(!on) // has to be on to receive messages + return + + if(is_freq_listening(signal)) // detect subspace signals + + //signal.data["done"] = 1 // mark the signal as being broadcasted since we're a broadcaster + signal.data["compression"] = 0 + + /* + // Search for the original signal and mark it as done as well + var/datum/signal/original = signal.data["original"] + if(original) + original.data["done"] = 1 + */ + + // For some reason level is both used as a list and not a list, and now it needs to be a list. + // Because this is a 'all in one' machine, we're gonna just cheat. + //signal.data["level"] = GLOB.using_map.contact_levels.Copy() + + if(signal.data["slow"] > 0) + sleep(signal.data["slow"]) // simulate the network lag if necessary + + /* ###### Broadcast a message using signal.data ###### */ + + var/datum/radio_frequency/connection = signal.data["connection"] + + var/list/forced_radios + for(var/datum/weakref/wr in linked_radios_weakrefs) + var/obj/item/radio/R = wr.resolve() + if(istype(R)) + LAZYDISTINCTADD(forced_radios, R) + + if(connection.frequency in ANTAG_FREQS) // if antag broadcast, just + Broadcast_Message(signal.data["connection"], signal.data["mob"], + signal.data["vmask"], signal.data["vmessage"], + signal.data["radio"], signal.data["message"], + signal.data["name"], signal.data["job"], + signal.data["realname"], signal.data["vname"], DATA_NORMAL, + signal.data["compression"], list(0), connection.frequency, + signal.data["verb"], signal.data["language"], forced_radios) + else + if(intercept) + Broadcast_Message(signal.data["connection"], signal.data["mob"], + signal.data["vmask"], signal.data["vmessage"], + signal.data["radio"], signal.data["message"], + signal.data["name"], signal.data["job"], + signal.data["realname"], signal.data["vname"], DATA_ANTAG, + signal.data["compression"], list(0), connection.frequency, + signal.data["verb"], signal.data["language"], forced_radios) + +//ERT version with unlimited range (doesn't even check) and uses no power, to enable ert comms to work anywhere. +/obj/machinery/telecomms/allinone/ert + use_power = USE_POWER_OFF + idle_power_usage = 0 + +/obj/machinery/telecomms/allinone/ert/receive_signal(datum/signal/signal) + if(!on) // has to be on to receive messages + return + + if(is_freq_listening(signal)) // detect subspace signals + + //signal.data["done"] = 1 // mark the signal as being broadcasted since we're a broadcaster + signal.data["compression"] = 0 + + /* + // Search for the original signal and mark it as done as well + var/datum/signal/original = signal.data["original"] + if(original) + original.data["done"] = 1 + */ + + // For some reason level is both used as a list and not a list, and now it needs to be a list. + // Because this is a 'all in one' machine, we're gonna just cheat. + //signal.data["level"] = GLOB.using_map.contact_levels.Copy() + + if(signal.data["slow"] > 0) + sleep(signal.data["slow"]) // simulate the network lag if necessary + + /* ###### Broadcast a message using signal.data ###### */ + + var/datum/radio_frequency/connection = signal.data["connection"] + + var/list/forced_radios + for(var/datum/weakref/wr in linked_radios_weakrefs) + var/obj/item/radio/R = wr.resolve() + if(istype(R)) + LAZYDISTINCTADD(forced_radios, R) + + if(connection.frequency in CENT_FREQS) // if ert broadcast, just + Broadcast_Message(signal.data["connection"], signal.data["mob"], + signal.data["vmask"], signal.data["vmessage"], + signal.data["radio"], signal.data["message"], + signal.data["name"], signal.data["job"], + signal.data["realname"], signal.data["vname"], DATA_NORMAL, + signal.data["compression"], list(0), connection.frequency, + signal.data["verb"], signal.data["language"], forced_radios) diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm index 29bf70a5b5a..709e15d77f1 100644 --- a/code/game/machinery/telecomms/broadcaster.dm +++ b/code/game/machinery/telecomms/broadcaster.dm @@ -122,161 +122,6 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept message_delay = 0 ..() - -/* - Basically just an empty shell for receiving and broadcasting radio messages. Not - very flexible, but it gets the job done. - NOTE: This AIO device listens on *every* zlevel (it does not even check) -*/ - -/obj/machinery/telecomms/allinone - name = "Telecommunications Mainframe" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "allinone" - desc = "A compact machine used for portable subspace telecommuniations processing." - density = 1 - use_power = USE_POWER_IDLE - idle_power_usage = 20 - anchored = 1 - machinetype = 6 - produces_heat = 0 - var/intercept = 0 // if nonzero, broadcasts all messages to syndicate channel - var/overmap_range = 0 //Same turf - - var/list/linked_radios_weakrefs = list() - -/obj/machinery/telecomms/allinone/proc/link_radio(var/obj/item/radio/R) - if(!istype(R)) - return - linked_radios_weakrefs |= WEAKREF(R) - -/obj/machinery/telecomms/allinone/receive_signal(datum/signal/signal) - - // Has to be on to receive messages - if(!on) - return - - // Why did you use this subtype? - if(!GLOB.using_map.use_overmap) - return - - // Someone else handling it? - if(signal.data["done"]) - return - - // Where are we able to hear from (and talk to, since we're AIO) anyway? - var/map_levels = GLOB.using_map.get_map_levels(z, TRUE, overmap_range) - - //Bluespace can skip this check - if(signal.transmission_method != TRANSMISSION_BLUESPACE) - var/list/signal_levels = list() - signal_levels += signal.data["level"] //If it's text/number, it'll be the only entry, if it's a list, it'll get combined - var/list/overlap = map_levels & signal_levels //Returns a list of similar levels - if(!overlap.len) - return - - if(is_freq_listening(signal)) // detect subspace signals - - signal.data["done"] = 1 // mark the signal as being broadcasted since we're a broadcaster - signal.data["compression"] = 0 // decompress since we're a processor - - // Search for the original signal and mark it as done as well - var/datum/signal/original = signal.data["original"] - if(original) - original.data["done"] = 1 - - // For some reason level is both used as a list and not a list, and now it needs to be a list. - signal.data["level"] = map_levels - - if(signal.data["slow"] > 0) - sleep(signal.data["slow"]) // simulate the network lag if necessary - - /* ###### Broadcast a message using signal.data ###### */ - - var/datum/radio_frequency/connection = signal.data["connection"] - - var/list/forced_radios - for(var/datum/weakref/wr in linked_radios_weakrefs) - var/obj/item/radio/R = wr.resolve() - if(istype(R)) - LAZYDISTINCTADD(forced_radios, R) - - Broadcast_Message( - signal.data["connection"], - signal.data["mob"], - signal.data["vmask"], - signal.data["vmessage"], - signal.data["radio"], - signal.data["message"], - signal.data["name"], - signal.data["job"], - signal.data["realname"], - signal.data["vname"], - DATA_NORMAL, - signal.data["compression"], - signal.data["level"], - connection.frequency, - signal.data["verb"], - signal.data["language"], - forced_radios - ) - -//Antag version with unlimited range (doesn't even check) and uses no power, to enable antag comms to work anywhere. -/obj/machinery/telecomms/allinone/antag - use_power = USE_POWER_OFF - idle_power_usage = 0 - -/obj/machinery/telecomms/allinone/antag/receive_signal(datum/signal/signal) - if(!on) // has to be on to receive messages - return - - if(is_freq_listening(signal)) // detect subspace signals - - //signal.data["done"] = 1 // mark the signal as being broadcasted since we're a broadcaster - signal.data["compression"] = 0 - - /* - // Search for the original signal and mark it as done as well - var/datum/signal/original = signal.data["original"] - if(original) - original.data["done"] = 1 - */ - - // For some reason level is both used as a list and not a list, and now it needs to be a list. - // Because this is a 'all in one' machine, we're gonna just cheat. - //signal.data["level"] = GLOB.using_map.contact_levels.Copy() - - if(signal.data["slow"] > 0) - sleep(signal.data["slow"]) // simulate the network lag if necessary - - /* ###### Broadcast a message using signal.data ###### */ - - var/datum/radio_frequency/connection = signal.data["connection"] - - var/list/forced_radios - for(var/datum/weakref/wr in linked_radios_weakrefs) - var/obj/item/radio/R = wr.resolve() - if(istype(R)) - LAZYDISTINCTADD(forced_radios, R) - - if(connection.frequency in ANTAG_FREQS) // if antag broadcast, just - Broadcast_Message(signal.data["connection"], signal.data["mob"], - signal.data["vmask"], signal.data["vmessage"], - signal.data["radio"], signal.data["message"], - signal.data["name"], signal.data["job"], - signal.data["realname"], signal.data["vname"], DATA_NORMAL, - signal.data["compression"], list(0), connection.frequency, - signal.data["verb"], signal.data["language"], forced_radios) - else - if(intercept) - Broadcast_Message(signal.data["connection"], signal.data["mob"], - signal.data["vmask"], signal.data["vmessage"], - signal.data["radio"], signal.data["message"], - signal.data["name"], signal.data["job"], - signal.data["realname"], signal.data["vname"], DATA_ANTAG, - signal.data["compression"], list(0), connection.frequency, - signal.data["verb"], signal.data["language"], forced_radios) - /** Here is the big, bad function that broadcasts a message given the appropriate diff --git a/code/game/machinery/telecomms/broadcaster_vr.dm b/code/game/machinery/telecomms/broadcaster_vr.dm deleted file mode 100644 index 775604bbf76..00000000000 --- a/code/game/machinery/telecomms/broadcaster_vr.dm +++ /dev/null @@ -1,46 +0,0 @@ -//ERT version with unlimited range (doesn't even check) and uses no power, to enable ert comms to work anywhere. -/obj/machinery/telecomms/allinone/ert - use_power = USE_POWER_OFF - idle_power_usage = 0 - -/obj/machinery/telecomms/allinone/ert/receive_signal(datum/signal/signal) - if(!on) // has to be on to receive messages - return - - if(is_freq_listening(signal)) // detect subspace signals - - //signal.data["done"] = 1 // mark the signal as being broadcasted since we're a broadcaster - signal.data["compression"] = 0 - - /* - // Search for the original signal and mark it as done as well - var/datum/signal/original = signal.data["original"] - if(original) - original.data["done"] = 1 - */ - - // For some reason level is both used as a list and not a list, and now it needs to be a list. - // Because this is a 'all in one' machine, we're gonna just cheat. - //signal.data["level"] = GLOB.using_map.contact_levels.Copy() - - if(signal.data["slow"] > 0) - sleep(signal.data["slow"]) // simulate the network lag if necessary - - /* ###### Broadcast a message using signal.data ###### */ - - var/datum/radio_frequency/connection = signal.data["connection"] - - var/list/forced_radios - for(var/weakref/wr in linked_radios_weakrefs) - var/obj/item/radio/R = wr.resolve() - if(istype(R)) - LAZYDISTINCTADD(forced_radios, R) - - if(connection.frequency in CENT_FREQS) // if ert broadcast, just - Broadcast_Message(signal.data["connection"], signal.data["mob"], - signal.data["vmask"], signal.data["vmessage"], - signal.data["radio"], signal.data["message"], - signal.data["name"], signal.data["job"], - signal.data["realname"], signal.data["vname"], DATA_NORMAL, - signal.data["compression"], list(0), connection.frequency, - signal.data["verb"], signal.data["language"], forced_radios) diff --git a/code/game/machinery/telecomms/bus.dm b/code/game/machinery/telecomms/bus.dm new file mode 100644 index 00000000000..42de08a0c72 --- /dev/null +++ b/code/game/machinery/telecomms/bus.dm @@ -0,0 +1,54 @@ +/* + The bus mainframe idles and waits for hubs to relay them signals. They act + as junctions for the network. + + They transfer uncompressed subspace packets to processor units, and then take + the processed packet to a server for logging. + + Link to a subspace hub if it can't send to a server. +*/ + +/obj/machinery/telecomms/bus + name = "Bus Mainframe" + icon_state = "bus" + desc = "A mighty piece of hardware used to send massive amounts of data quickly." + density = 1 + anchored = 1 + use_power = USE_POWER_IDLE + idle_power_usage = 1000 + machinetype = 2 + circuit = /obj/item/circuitboard/telecomms/bus + netspeed = 40 + var/change_frequency = 0 + +/obj/machinery/telecomms/bus/Initialize(mapload) + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/bus/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + + if(is_freq_listening(signal)) + + if(change_frequency) + signal.frequency = change_frequency + + if(!istype(machine_from, /obj/machinery/telecomms/processor) && machine_from != src) // Signal must be ready (stupid assuming machine), let's send it + // send to one linked processor unit + var/send_to_processor = relay_information(signal, "/obj/machinery/telecomms/processor") + + if(send_to_processor) + return + // failed to send to a processor, relay information anyway + signal.data["slow"] += rand(1, 5) // slow the signal down only slightly + src.receive_information(signal, src) + + // Try sending it! + var/list/try_send = list("/obj/machinery/telecomms/server", "/obj/machinery/telecomms/hub", "/obj/machinery/telecomms/broadcaster", "/obj/machinery/telecomms/bus") + var/i = 0 + for(var/send in try_send) + if(i) + signal.data["slow"] += rand(0, 1) // slow the signal down only slightly + i++ + var/can_send = relay_information(signal, send) + if(can_send) + break diff --git a/code/game/machinery/telecomms/hub.dm b/code/game/machinery/telecomms/hub.dm new file mode 100644 index 00000000000..6644fa720ee --- /dev/null +++ b/code/game/machinery/telecomms/hub.dm @@ -0,0 +1,36 @@ +/* + The HUB idles until it receives information. It then passes on that information + depending on where it came from. + + This is the heart of the Telecommunications Network, sending information where it + is needed. It mainly receives information from long-distance Relays and then sends + that information to be processed. Afterwards it gets the uncompressed information + from Servers/Buses and sends that back to the relay, to then be broadcasted. +*/ + +/obj/machinery/telecomms/hub + name = "Telecommunication Hub" + icon_state = "hub" + desc = "A mighty piece of hardware used to send/receive massive amounts of data." + density = 1 + anchored = 1 + use_power = USE_POWER_IDLE + idle_power_usage = 1600 + machinetype = 7 + circuit = /obj/item/circuitboard/telecomms/hub + long_range_link = 1 + netspeed = 40 + +/obj/machinery/telecomms/hub/Initialize(mapload) + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/hub/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + if(is_freq_listening(signal)) + if(istype(machine_from, /obj/machinery/telecomms/receiver)) + //If the signal is compressed, send it to the bus. + relay_information(signal, "/obj/machinery/telecomms/bus", 1) // ideally relay the copied information to bus units + else + // Get a list of relays that we're linked to, then send the signal to their levels. + relay_information(signal, "/obj/machinery/telecomms/relay", 1) + relay_information(signal, "/obj/machinery/telecomms/broadcaster", 1) // Send it to a broadcaster. diff --git a/code/game/machinery/telecomms/logbrowser.dm b/code/game/machinery/telecomms/logbrowser.dm index 6ba60522e02..6ff4f804946 100644 --- a/code/game/machinery/telecomms/logbrowser.dm +++ b/code/game/machinery/telecomms/logbrowser.dm @@ -4,7 +4,7 @@ /obj/machinery/computer/telecomms/server name = "Telecommunications Server Monitor" - desc = "View communication logs here. Translation not guaranteed." + desc = "View communication logs and operate triangulation systems here. Translation not guaranteed." icon_screen = "comm_logs" var/list/servers = list() // the servers located by the computer @@ -37,7 +37,9 @@ data["selectedServer"] = list( "id" = SelectedServer.id, "totalTraffic" = SelectedServer.totaltraffic, - "logs" = list() + "logs" = list(), + "triangulating" = SelectedServer.triangulating, + "triangulation" = SelectedServer.triangulation_data(), ) var/i = 0 @@ -138,6 +140,10 @@ temp = null . = TRUE + if("toggle_triangulation") + SelectedServer.set_triangulating(!SelectedServer.triangulating) + return TRUE + /obj/machinery/computer/telecomms/server/emag_act(var/remaining_charges, var/mob/user) if(!emagged) playsound(src, 'sound/effects/sparks4.ogg', 75, 1) diff --git a/code/game/machinery/telecomms/processor.dm b/code/game/machinery/telecomms/processor.dm new file mode 100644 index 00000000000..d05509e1f47 --- /dev/null +++ b/code/game/machinery/telecomms/processor.dm @@ -0,0 +1,39 @@ +/* + The processor is a very simple machine that decompresses subspace signals and + transfers them back to the original bus. It is essential in producing audible + data. + + Link to servers if bus is not present +*/ + +/obj/machinery/telecomms/processor + name = "Processor Unit" + icon_state = "processor" + desc = "This machine is used to process large quantities of information." + density = 1 + anchored = 1 + use_power = USE_POWER_IDLE + idle_power_usage = 600 + machinetype = 3 + delay = 5 + circuit = /obj/item/circuitboard/telecomms/processor + var/process_mode = 1 // 1 = Uncompress Signals, 0 = Compress Signals + +/obj/machinery/telecomms/processor/Initialize(mapload) + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/processor/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + + if(is_freq_listening(signal)) + + if(process_mode) + signal.data["compression"] = 0 // uncompress subspace signal + else + signal.data["compression"] = 100 // even more compressed signal + + if(istype(machine_from, /obj/machinery/telecomms/bus)) + relay_direct_information(signal, machine_from) // send the signal back to the machine + else // no bus detected - send the signal to servers instead + signal.data["slow"] += rand(5, 10) // slow the signal down + relay_information(signal, "/obj/machinery/telecomms/server") diff --git a/code/game/machinery/telecomms/receiver.dm b/code/game/machinery/telecomms/receiver.dm new file mode 100644 index 00000000000..83c7e7e6721 --- /dev/null +++ b/code/game/machinery/telecomms/receiver.dm @@ -0,0 +1,83 @@ +/* + The receiver idles and receives messages from subspace-compatible radio equipment; + primarily headsets. They then just relay this information to all linked devices, + which can would probably be network hubs. + + Link to Processor Units in case receiver can't send to bus units. +*/ + +/obj/machinery/telecomms/receiver + name = "Subspace Receiver" + icon_state = "broadcast receiver" + desc = "This machine has a dish-like shape and green lights. It is designed to detect and process subspace radio activity." + density = 1 + anchored = 1 + use_power = USE_POWER_IDLE + idle_power_usage = 600 + machinetype = 1 + produces_heat = 0 + circuit = /obj/item/circuitboard/telecomms/receiver + //Vars only used if you're using the overmap + var/overmap_range = 0 + var/overmap_range_min = 0 + var/overmap_range_max = 5 + + var/list/linked_radios_weakrefs = list() + +/obj/machinery/telecomms/receiver/Initialize(mapload) + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/receiver/proc/link_radio(var/obj/item/radio/R) + if(!istype(R)) + return + linked_radios_weakrefs |= WEAKREF(R) + +/obj/machinery/telecomms/receiver/receive_signal(datum/signal/signal) + if(!on) // has to be on to receive messages + return + if(!signal) + return + if(!check_receive_level(signal)) + return + + if(signal.transmission_method == TRANSMISSION_SUBSPACE) + + if(is_freq_listening(signal)) // detect subspace signals + + //Remove the level and then start adding levels that it is being broadcasted in. + signal.data["level"] = list() + + var/can_send = relay_information(signal, "/obj/machinery/telecomms/hub") // ideally relay the copied information to relays + if(!can_send) + relay_information(signal, "/obj/machinery/telecomms/bus") // Send it to a bus instead, if it's linked to one + +/obj/machinery/telecomms/receiver/proc/check_receive_level(datum/signal/signal) + // If it's a direct message from a bluespace radio, we eat it and convert it into a subspace signal locally + if(signal.transmission_method == TRANSMISSION_BLUESPACE) + var/obj/item/radio/R = signal.data["radio"] + + //Who're you? + if(!(WEAKREF(R) in linked_radios_weakrefs)) + signal.data["reject"] = 1 + return 0 + + //We'll resend this for you + signal.data["level"] = z + signal.transmission_method = TRANSMISSION_SUBSPACE + return 1 + + //Where can we hear? + var/list/listening_levels = GLOB.using_map.get_map_levels(listening_level, TRUE, overmap_range) + + // We couldn't 'hear' it, maybe a relay linked to our hub can 'hear' it + if(!(signal.data["level"] in listening_levels)) + for(var/obj/machinery/telecomms/hub/H in links) + var/list/relayed_levels = list() + for(var/obj/machinery/telecomms/relay/R in H.links) + if(R.can_receive(signal)) + relayed_levels |= R.listening_level + if(signal.data["level"] in relayed_levels) + return 1 + return 0 + return 1 diff --git a/code/game/machinery/telecomms/relay.dm b/code/game/machinery/telecomms/relay.dm new file mode 100644 index 00000000000..811435255c0 --- /dev/null +++ b/code/game/machinery/telecomms/relay.dm @@ -0,0 +1,56 @@ +/* + The relay idles until it receives information. It then passes on that information + depending on where it came from. + + The relay is needed in order to send information pass Z levels. It must be linked + with a HUB, the only other machine that can send/receive pass Z levels. +*/ + +/obj/machinery/telecomms/relay + name = "Telecommunication Relay" + icon_state = "relay" + desc = "A mighty piece of hardware used to send massive amounts of data far away." + density = 1 + anchored = 1 + use_power = USE_POWER_IDLE + idle_power_usage = 600 + machinetype = 8 + produces_heat = 0 + circuit = /obj/item/circuitboard/telecomms/relay + netspeed = 5 + long_range_link = 1 + var/broadcasting = 1 + var/receiving = 1 + +/obj/machinery/telecomms/relay/Initialize(mapload) + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/relay/onTransitZ(old_z, new_z) + . = ..() + listening_level = z + +/obj/machinery/telecomms/relay/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + + // Add our level and send it back + if(can_send(signal)) + signal.data["level"] |= GLOB.using_map.get_map_levels(listening_level) + +// Checks to see if it can send/receive. + +/obj/machinery/telecomms/relay/proc/can(datum/signal/signal) + if(!on) + return 0 + if(!is_freq_listening(signal)) + return 0 + return 1 + +/obj/machinery/telecomms/relay/proc/can_send(datum/signal/signal) + if(!can(signal)) + return 0 + return broadcasting + +/obj/machinery/telecomms/relay/proc/can_receive(datum/signal/signal) + if(!can(signal)) + return 0 + return receiving diff --git a/code/game/machinery/telecomms/server.dm b/code/game/machinery/telecomms/server.dm new file mode 100644 index 00000000000..6bd63a05c3e --- /dev/null +++ b/code/game/machinery/telecomms/server.dm @@ -0,0 +1,231 @@ +/* + The server logs all traffic and signal data. Once it records the signal, it sends + it to the subspace broadcaster. + + Store a maximum of 100 logs and then deletes them. +*/ + + +/obj/machinery/telecomms/server + name = "Telecommunication Server" + icon_state = "comm_server" + desc = "A machine used to store data and network statistics." + density = 1 + anchored = 1 + use_power = USE_POWER_IDLE + idle_power_usage = 300 + machinetype = 4 + circuit = /obj/item/circuitboard/telecomms/server + var/list/log_entries = list() + var/logs = 0 // number of logs + var/totaltraffic = 0 // gigabytes (if > 1024, divide by 1024 -> terrabytes) + + var/obj/item/radio/headset/server_radio = null + + //? triangulation + /// enabled? + var/triangulating = FALSE + /// tracking source tags to /datum/network_triangulation + var/list/triangulation + /// accuracy loss per decisecond + var/triangulation_falloff = 1 / 60 + /// accuracy loss per tile + var/triangulation_stickyness = 5 / 20 + /// triangulation efficiency multiplier + var/triangulation_efficiency = 1 + +/obj/machinery/telecomms/server/Initialize(mapload) + . = ..() + server_radio = new() + +/obj/machinery/telecomms/server/Initialize(mapload) + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/server/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + + if(signal.data["message"]) + + if(is_freq_listening(signal)) + + if(traffic > 0) + totaltraffic += traffic // add current traffic to total traffic + + //Is this a test signal? Bypass logging + if(signal.data["type"] != SIGNAL_TEST) + if(signal.data["radio"] && triangulating) + triangulate(signal.data["radio"], update_name = signal.data["name"]) + + // If signal has a message and appropriate frequency + + update_logs() + + var/datum/comm_log_entry/log = new + var/mob/M = signal.data["mob"] + + // Copy the signal.data entries we want + log.parameters["mobtype"] = signal.data["mobtype"] + log.parameters["job"] = signal.data["job"] + log.parameters["key"] = signal.data["key"] + log.parameters["vmessage"] = signal.data["message"] + log.parameters["vname"] = signal.data["vname"] + log.parameters["message"] = signal.data["message"] + log.parameters["name"] = signal.data["name"] + log.parameters["realname"] = signal.data["realname"] + log.parameters["timecode"] = worldtime2stationtime(world.time) + + var/race = "unknown" + if(ishuman(M)) + var/mob/living/carbon/human/H = M + race = "[H.species.name]" + log.parameters["intelligible"] = 1 + else if(isbrain(M)) + race = "Brain" + log.parameters["intelligible"] = 1 + else if(M.isMonkey()) + race = "Monkey" + else if(issilicon(M)) + race = "Artificial Life" + log.parameters["intelligible"] = 1 + else if(isslime(M)) + race = "Slime" + else if(isanimal(M)) + race = "Domestic Animal" + + log.parameters["race"] = race + + if(!istype(M, /mob/new_player) && M) + log.parameters["uspeech"] = M.universal_speak + else + log.parameters["uspeech"] = 0 + + // If the signal is still compressed, make the log entry gibberish + if(signal.data["compression"] > 0) + log.parameters["message"] = Gibberish(signal.data["message"], signal.data["compression"] + 50) + log.parameters["job"] = Gibberish(signal.data["job"], signal.data["compression"] + 50) + log.parameters["name"] = Gibberish(signal.data["name"], signal.data["compression"] + 50) + log.parameters["realname"] = Gibberish(signal.data["realname"], signal.data["compression"] + 50) + log.parameters["vname"] = Gibberish(signal.data["vname"], signal.data["compression"] + 50) + log.input_type = "Corrupt File" + + // Log and store everything that needs to be logged + log_entries.Add(log) + logs++ + signal.data["server"] = src + + // Give the log a name + var/identifier = num2text( rand(-1000,1000) + world.time ) + log.name = "data packet ([md5(identifier)])" + + var/can_send = relay_information(signal, "/obj/machinery/telecomms/hub") + if(!can_send) + relay_information(signal, "/obj/machinery/telecomms/broadcaster") + +/obj/machinery/telecomms/server/proc/update_logs() + // start deleting the very first log entry + if(logs >= 400) + for(var/i = 1, i <= logs, i++) // locate the first garbage collectable log entry and remove it + var/datum/comm_log_entry/L = log_entries[i] + if(L.garbage_collector) + log_entries.Remove(L) + logs-- + break + +/obj/machinery/telecomms/server/proc/add_entry(var/content, var/input) + var/datum/comm_log_entry/log = new + var/identifier = num2text( rand(-1000,1000) + world.time ) + log.name = "[input] ([md5(identifier)])" + log.input_type = input + log.parameters["message"] = content + log.parameters["timecode"] = stationtime2text() + log_entries.Add(log) + update_logs() + +/obj/machinery/telecomms/server/proc/triangulation_data() + . = list() + if(!triangulation) + return + for(var/key in triangulation) + var/datum/network_triangulation/T = triangulation[key] + . += list(list( + "name" = T.scan_name, + "x" = T.random_x, + "y" = T.random_y, + "z" = SSmapping.level_id(T.random_z), + "accuracy" = T.accuracy, + "last" = world.time - T.last_updated, + "tag" = key, + )) + +/obj/machinery/telecomms/server/proc/triangulate(atom/movable/victim, reduction_factor = 1.8, update_name) + if(isnull(triangulation)) + triangulation = list() + + reduction_factor *= triangulation_efficiency + + // if victim isn't even on a turf... + var/turf/location = get_turf(victim) + if(isnull(location)) + return + + // generate tag + var/triangulation_tag = ismob(victim)? victim.tag : ref(victim) + + // grab or make entry, update location + var/datum/network_triangulation/entry = triangulation[triangulation_tag] + if(!entry) + triangulation[triangulation_tag] = (entry = new) + // starting at.. + entry.accuracy = round(max(world.maxx, world.maxy) / reduction_factor, 1) + else + // narrow down + var/time_falloff = max(world.time - entry.last_updated, 0) * triangulation_falloff + var/dist_falloff = entry.last_turf ? get_dist(entry.last_turf, location) * triangulation_stickyness : 0 + entry.accuracy = min( + round(max(world.maxx, world.maxy) / reduction_factor, 1), // don't get worse than a first attempt + (entry.accuracy + time_falloff + dist_falloff) / reduction_factor + ) + + // update name + if(update_name) + entry.scan_name = update_name + + entry.last_turf = location + entry.last_updated = world.time + entry.randomize(location) + +/obj/machinery/telecomms/server/proc/set_triangulating(state) + triangulating = state + if(!state) + triangulation = null + +// Simple log entry datum + +/datum/comm_log_entry + var/parameters = list() // carbon-copy to signal.data[] + var/name = "data packet (#)" + var/garbage_collector = 1 // if set to 0, will not be garbage collected + var/input_type = "Speech File" + +/datum/network_triangulation + /// name of target + var/scan_name + /// last x + var/random_x + /// last y + var/random_y + /// last real z index + var/random_z + /// last accuracy + var/accuracy + /// last update time + var/last_updated + /// last turf + var/turf/last_turf + +/datum/network_triangulation/proc/randomize(turf/real_loc) + var/angle = rand(0, 360) + var/dist = accuracy * sqrt(rand(1, 10000) * 0.0001) + random_x = round(cos(angle) * dist, 1) + real_loc.x + random_y = round(sin(angle) * dist, 1) + real_loc.y + random_z = real_loc.z diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm deleted file mode 100644 index 905a3a72997..00000000000 --- a/code/game/machinery/telecomms/telecomunications.dm +++ /dev/null @@ -1,699 +0,0 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/* - Hello, friends, this is Doohl from sexylands. You may be wondering what this - monstrous code file is. Sit down, boys and girls, while I tell you the tale. - - - The machines defined in this file were designed to be compatible with any radio - signals, provided they use subspace transmission. Currently they are only used for - headsets, but they can eventually be outfitted for real COMPUTER networks. This - is just a skeleton, ladies and gentlemen. - - Look at radio.dm for the prequel to this code. -*/ - -/obj/machinery/telecomms - icon = 'icons/obj/stationobjs.dmi' - ///List of machines this machine is linked to. - var/list/links = list() - ///Value increases as traffic increases. - var/traffic = 0 - ///How much traffic to lose per tick (50 gigabytes/second * netspeed). - var/netspeed = 5 - ///List of text/number values to link with. - var/list/autolinkers = list() - ///Identification string. - var/id = "NULL" - ///The network of the machinery. - var/network = "NULL" - //List of frequencies to tune into: if none, will listen to all. - var/list/freq_listening = list() - ///Just a hacky way of preventing alike machines from pairing. - var/machinetype = 0 - ///Is it toggled on. //Not sure why we have this when we have var/on. - var/toggled = TRUE - var/on = TRUE - ///Basically HP, loses integrity by heat. - integrity = 100 - ///Whether the machine will produce heat when on. - var/produces_heat = TRUE - ///How many process() ticks to delay per heat. - var/delay = 10 - ///Can you link it across Z levels or on the otherside of the map? (Relay & Hub) - var/long_range_link = FALSE - ///Is it a hidden machine? - var/hide = 0 - ///0 = auto set in New() - this is the z level that the machine is listening to. - var/listening_level = 0 - - -/obj/machinery/telecomms/proc/relay_information(datum/signal/signal, filter, copysig, amount = 20) - // relay signal to all linked machinery that are of type [filter]. If signal has been sent [amount] times, stop sending - - if(!on) - return - //TO_WORLD("[src] ([src.id]) - [signal.debug_print()]") - var/send_count = 0 - - signal.data["slow"] += rand(0, round((100-integrity))) // apply some lag based on integrity - - /* - // Edit by Atlantis: Commented out as emergency fix due to causing extreme delays in communications. - // Apply some lag based on traffic rates - var/netlag = round(traffic / 50) - if(netlag > signal.data["slow"]) - signal.data["slow"] = netlag - */ -// Loop through all linked machines and send the signal or copy. - for(var/obj/machinery/telecomms/machine in links) - if(filter && !istype( machine, text2path(filter) )) - continue - if(!machine.on) - continue - if(amount && send_count >= amount) - break - if(machine.loc.z != listening_level) - if(long_range_link == 0 && machine.long_range_link == 0) - continue - // If we're sending a copy, be sure to create the copy for EACH machine and paste the data - var/datum/signal/copy - if(copysig) - copy = new - copy.transmission_method = TRANSMISSION_SUBSPACE - copy.frequency = signal.frequency - copy.data = signal.data.Copy() - - // Keep the "original" signal constant - if(!signal.data["original"]) - copy.data["original"] = signal - else - copy.data["original"] = signal.data["original"] - - send_count++ - if(machine.is_freq_listening(signal)) - machine.traffic++ - - if(copysig && copy) - machine.receive_information(copy, src) - else - machine.receive_information(signal, src) - - - if(send_count > 0 && is_freq_listening(signal)) - traffic++ - - return send_count - -/obj/machinery/telecomms/proc/relay_direct_information(datum/signal/signal, obj/machinery/telecomms/machine) - // send signal directly to a machine - machine.receive_information(signal, src) - -/obj/machinery/telecomms/proc/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - // receive information from linked machinery - -/obj/machinery/telecomms/proc/is_freq_listening(datum/signal/signal) - // return 1 if found, 0 if not found - if(!signal) - return 0 - if((signal.frequency in freq_listening) || (!freq_listening.len)) - return 1 - else - return 0 - -/obj/machinery/telecomms/Initialize(mapload) - GLOB.telecomms_list += src - . = ..() - - //Set the listening_level if there's none. - if(!listening_level) - //Defaults to our Z level! - var/turf/position = get_turf(src) - listening_level = position.z - return INITIALIZE_HINT_LATELOAD - -/obj/machinery/telecomms/LateInitialize() - if(autolinkers.len) - // Links nearby machines - if(!long_range_link) - for(var/obj/machinery/telecomms/T in orange(20, src)) - add_link(T) - else - for(var/obj/machinery/telecomms/T in GLOB.telecomms_list) - add_link(T) - return ..() - -/obj/machinery/telecomms/Destroy() - GLOB.telecomms_list -= src - for(var/obj/machinery/telecomms/comm in GLOB.telecomms_list) - comm.links -= src - links = list() - ..() - -// Used in auto linking -/obj/machinery/telecomms/proc/add_link(var/obj/machinery/telecomms/T) - var/pos_z = get_z(src) - var/tpos_z = get_z(T) - if((pos_z == tpos_z) || (src.long_range_link && T.long_range_link)) - for(var/x in autolinkers) - if(T.autolinkers.Find(x)) - if(src != T) - links |= T - -/obj/machinery/telecomms/update_icon() - if(on) - icon_state = initial(icon_state) - else - icon_state = "[initial(icon_state)]_off" - -/obj/machinery/telecomms/proc/update_power() - if(toggled) - if(machine_stat & (BROKEN|NOPOWER|EMPED) || integrity <= 0) // if powered, on. if not powered, off. if too damaged, off - on = FALSE - else - on = TRUE - else - on = FALSE - -/obj/machinery/telecomms/process() - update_power() - - // Check heat and generate some - checkheat() - - // Update the icon - update_icon() - - if(traffic > 0) - traffic -= netspeed - -/obj/machinery/telecomms/emp_act(severity) - if(prob(100/severity)) - if(!(machine_stat & EMPED)) - machine_stat |= EMPED - var/duration = (300 * 10)/severity - spawn(rand(duration - 20, duration + 20)) // Takes a long time for the machines to reboot. - machine_stat &= ~EMPED - ..() - -/obj/machinery/telecomms/proc/checkheat() - // Checks heat from the environment and applies any integrity damage - var/damage_chance = 0 // Percent based chance of applying 1 integrity damage this tick - switch(loc.return_temperature()) - if((T0C + 40) to (T0C + 70)) // 40C-70C, minor overheat, 10% chance of taking damage - damage_chance = 10 - if((T0C + 70) to (T0C + 130)) // 70C-130C, major overheat, 25% chance of taking damage - damage_chance = 25 - if((T0C + 130) to (T0C + 200)) // 130C-200C, dangerous overheat, 50% chance of taking damage - damage_chance = 50 - if((T0C + 200) to INFINITY) // More than 200C, INFERNO. Takes damage every tick. - damage_chance = 100 - if (damage_chance && prob(damage_chance)) - integrity = clamp( integrity - 1, 0, 100) - - if(delay > 0) - delay-- - else if(on) - produce_heat() - delay = initial(delay) - -/obj/machinery/telecomms/proc/produce_heat() - if (!produces_heat) - return - - if (!use_power) - return - - if(!(machine_stat & (NOPOWER|BROKEN))) - var/turf/simulated/L = loc - if(istype(L)) - var/datum/gas_mixture/env = L.return_air() - - var/transfer_moles = 0.25 * env.total_moles - - var/datum/gas_mixture/removed = env.remove(transfer_moles) - - if(removed) - - var/heat_produced = idle_power_usage //obviously can't produce more heat than the machine draws from it's power source - if (traffic <= 0) - heat_produced *= 0.30 //if idle, produce less heat. - - removed.adjust_thermal_energy(heat_produced) - - env.merge(removed) -/* - The receiver idles and receives messages from subspace-compatible radio equipment; - primarily headsets. They then just relay this information to all linked devices, - which can would probably be network hubs. - - Link to Processor Units in case receiver can't send to bus units. -*/ - -/obj/machinery/telecomms/receiver - name = "Subspace Receiver" - icon_state = "broadcast receiver" - desc = "This machine has a dish-like shape and green lights. It is designed to detect and process subspace radio activity." - density = 1 - anchored = 1 - use_power = USE_POWER_IDLE - idle_power_usage = 600 - machinetype = 1 - produces_heat = 0 - circuit = /obj/item/circuitboard/telecomms/receiver - //Vars only used if you're using the overmap - var/overmap_range = 0 - var/overmap_range_min = 0 - var/overmap_range_max = 5 - - var/list/linked_radios_weakrefs = list() - -/obj/machinery/telecomms/receiver/Initialize(mapload) - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/receiver/proc/link_radio(var/obj/item/radio/R) - if(!istype(R)) - return - linked_radios_weakrefs |= WEAKREF(R) - -/obj/machinery/telecomms/receiver/receive_signal(datum/signal/signal) - if(!on) // has to be on to receive messages - return - if(!signal) - return - if(!check_receive_level(signal)) - return - - if(signal.transmission_method == TRANSMISSION_SUBSPACE) - - if(is_freq_listening(signal)) // detect subspace signals - - //Remove the level and then start adding levels that it is being broadcasted in. - signal.data["level"] = list() - - var/can_send = relay_information(signal, "/obj/machinery/telecomms/hub") // ideally relay the copied information to relays - if(!can_send) - relay_information(signal, "/obj/machinery/telecomms/bus") // Send it to a bus instead, if it's linked to one - -/obj/machinery/telecomms/receiver/proc/check_receive_level(datum/signal/signal) - // If it's a direct message from a bluespace radio, we eat it and convert it into a subspace signal locally - if(signal.transmission_method == TRANSMISSION_BLUESPACE) - var/obj/item/radio/R = signal.data["radio"] - - //Who're you? - if(!(WEAKREF(R) in linked_radios_weakrefs)) - signal.data["reject"] = 1 - return 0 - - //We'll resend this for you - signal.data["level"] = z - signal.transmission_method = TRANSMISSION_SUBSPACE - return 1 - - //Where can we hear? - var/list/listening_levels = GLOB.using_map.get_map_levels(listening_level, TRUE, overmap_range) - - // We couldn't 'hear' it, maybe a relay linked to our hub can 'hear' it - if(!(signal.data["level"] in listening_levels)) - for(var/obj/machinery/telecomms/hub/H in links) - var/list/relayed_levels = list() - for(var/obj/machinery/telecomms/relay/R in H.links) - if(R.can_receive(signal)) - relayed_levels |= R.listening_level - if(signal.data["level"] in relayed_levels) - return 1 - return 0 - return 1 - - -/* - The HUB idles until it receives information. It then passes on that information - depending on where it came from. - - This is the heart of the Telecommunications Network, sending information where it - is needed. It mainly receives information from long-distance Relays and then sends - that information to be processed. Afterwards it gets the uncompressed information - from Servers/Buses and sends that back to the relay, to then be broadcasted. -*/ - -/obj/machinery/telecomms/hub - name = "Telecommunication Hub" - icon_state = "hub" - desc = "A mighty piece of hardware used to send/receive massive amounts of data." - density = 1 - anchored = 1 - use_power = USE_POWER_IDLE - idle_power_usage = 1600 - machinetype = 7 - circuit = /obj/item/circuitboard/telecomms/hub - long_range_link = 1 - netspeed = 40 - -/obj/machinery/telecomms/hub/Initialize(mapload) - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/hub/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - if(is_freq_listening(signal)) - if(istype(machine_from, /obj/machinery/telecomms/receiver)) - //If the signal is compressed, send it to the bus. - relay_information(signal, "/obj/machinery/telecomms/bus", 1) // ideally relay the copied information to bus units - else - // Get a list of relays that we're linked to, then send the signal to their levels. - relay_information(signal, "/obj/machinery/telecomms/relay", 1) - relay_information(signal, "/obj/machinery/telecomms/broadcaster", 1) // Send it to a broadcaster. - - -/* - The relay idles until it receives information. It then passes on that information - depending on where it came from. - - The relay is needed in order to send information pass Z levels. It must be linked - with a HUB, the only other machine that can send/receive pass Z levels. -*/ - -/obj/machinery/telecomms/relay - name = "Telecommunication Relay" - icon_state = "relay" - desc = "A mighty piece of hardware used to send massive amounts of data far away." - density = 1 - anchored = 1 - use_power = USE_POWER_IDLE - idle_power_usage = 600 - machinetype = 8 - produces_heat = 0 - circuit = /obj/item/circuitboard/telecomms/relay - netspeed = 5 - long_range_link = 1 - var/broadcasting = 1 - var/receiving = 1 - -/obj/machinery/telecomms/relay/Initialize(mapload) - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/relay/onTransitZ(old_z, new_z) - . = ..() - listening_level = z - -/obj/machinery/telecomms/relay/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - - // Add our level and send it back - if(can_send(signal)) - signal.data["level"] |= GLOB.using_map.get_map_levels(listening_level) - -// Checks to see if it can send/receive. - -/obj/machinery/telecomms/relay/proc/can(datum/signal/signal) - if(!on) - return 0 - if(!is_freq_listening(signal)) - return 0 - return 1 - -/obj/machinery/telecomms/relay/proc/can_send(datum/signal/signal) - if(!can(signal)) - return 0 - return broadcasting - -/obj/machinery/telecomms/relay/proc/can_receive(datum/signal/signal) - if(!can(signal)) - return 0 - return receiving - -/* - The bus mainframe idles and waits for hubs to relay them signals. They act - as junctions for the network. - - They transfer uncompressed subspace packets to processor units, and then take - the processed packet to a server for logging. - - Link to a subspace hub if it can't send to a server. -*/ - -/obj/machinery/telecomms/bus - name = "Bus Mainframe" - icon_state = "bus" - desc = "A mighty piece of hardware used to send massive amounts of data quickly." - density = 1 - anchored = 1 - use_power = USE_POWER_IDLE - idle_power_usage = 1000 - machinetype = 2 - circuit = /obj/item/circuitboard/telecomms/bus - netspeed = 40 - var/change_frequency = 0 - -/obj/machinery/telecomms/bus/Initialize(mapload) - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/bus/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - - if(is_freq_listening(signal)) - - if(change_frequency) - signal.frequency = change_frequency - - if(!istype(machine_from, /obj/machinery/telecomms/processor) && machine_from != src) // Signal must be ready (stupid assuming machine), let's send it - // send to one linked processor unit - var/send_to_processor = relay_information(signal, "/obj/machinery/telecomms/processor") - - if(send_to_processor) - return - // failed to send to a processor, relay information anyway - signal.data["slow"] += rand(1, 5) // slow the signal down only slightly - src.receive_information(signal, src) - - // Try sending it! - var/list/try_send = list("/obj/machinery/telecomms/server", "/obj/machinery/telecomms/hub", "/obj/machinery/telecomms/broadcaster", "/obj/machinery/telecomms/bus") - var/i = 0 - for(var/send in try_send) - if(i) - signal.data["slow"] += rand(0, 1) // slow the signal down only slightly - i++ - var/can_send = relay_information(signal, send) - if(can_send) - break - - - -/* - The processor is a very simple machine that decompresses subspace signals and - transfers them back to the original bus. It is essential in producing audible - data. - - Link to servers if bus is not present -*/ - -/obj/machinery/telecomms/processor - name = "Processor Unit" - icon_state = "processor" - desc = "This machine is used to process large quantities of information." - density = 1 - anchored = 1 - use_power = USE_POWER_IDLE - idle_power_usage = 600 - machinetype = 3 - delay = 5 - circuit = /obj/item/circuitboard/telecomms/processor - var/process_mode = 1 // 1 = Uncompress Signals, 0 = Compress Signals - -/obj/machinery/telecomms/processor/Initialize(mapload) - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/processor/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - - if(is_freq_listening(signal)) - - if(process_mode) - signal.data["compression"] = 0 // uncompress subspace signal - else - signal.data["compression"] = 100 // even more compressed signal - - if(istype(machine_from, /obj/machinery/telecomms/bus)) - relay_direct_information(signal, machine_from) // send the signal back to the machine - else // no bus detected - send the signal to servers instead - signal.data["slow"] += rand(5, 10) // slow the signal down - relay_information(signal, "/obj/machinery/telecomms/server") - - -/* - The server logs all traffic and signal data. Once it records the signal, it sends - it to the subspace broadcaster. - - Store a maximum of 100 logs and then deletes them. -*/ - - -/obj/machinery/telecomms/server - name = "Telecommunication Server" - icon_state = "comm_server" - desc = "A machine used to store data and network statistics." - density = 1 - anchored = 1 - use_power = USE_POWER_IDLE - idle_power_usage = 300 - machinetype = 4 - circuit = /obj/item/circuitboard/telecomms/server - var/list/log_entries = list() - var/list/stored_names = list() - var/list/TrafficActions = list() - var/logs = 0 // number of logs - var/totaltraffic = 0 // gigabytes (if > 1024, divide by 1024 -> terrabytes) - - var/list/memory = list() // stored memory - var/rawcode = "" // the code to compile (raw text) - var/autoruncode = 0 // 1 if the code is set to run every time a signal is picked up - - var/encryption = "null" // encryption key: ie "password" - var/salt = "null" // encryption salt: ie "123comsat" - // would add up to md5("password123comsat") - var/obj/item/radio/headset/server_radio = null - -/obj/machinery/telecomms/server/Initialize(mapload) - . = ..() - server_radio = new() - -/obj/machinery/telecomms/server/Initialize(mapload) - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/server/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - - if(signal.data["message"]) - - if(is_freq_listening(signal)) - - if(traffic > 0) - totaltraffic += traffic // add current traffic to total traffic - - //Is this a test signal? Bypass logging - if(signal.data["type"] != SIGNAL_TEST) - - // If signal has a message and appropriate frequency - - update_logs() - - var/datum/comm_log_entry/log = new - var/mob/M = signal.data["mob"] - - // Copy the signal.data entries we want - log.parameters["mobtype"] = signal.data["mobtype"] - log.parameters["job"] = signal.data["job"] - log.parameters["key"] = signal.data["key"] - log.parameters["vmessage"] = signal.data["message"] - log.parameters["vname"] = signal.data["vname"] - log.parameters["message"] = signal.data["message"] - log.parameters["name"] = signal.data["name"] - log.parameters["realname"] = signal.data["realname"] - log.parameters["timecode"] = worldtime2stationtime(world.time) - - var/race = "unknown" - if(ishuman(M)) - var/mob/living/carbon/human/H = M - race = "[H.species.name]" - log.parameters["intelligible"] = 1 - else if(isbrain(M)) - race = "Brain" - log.parameters["intelligible"] = 1 - else if(M.isMonkey()) - race = "Monkey" - else if(issilicon(M)) - race = "Artificial Life" - log.parameters["intelligible"] = 1 - else if(isslime(M)) - race = "Slime" - else if(isanimal(M)) - race = "Domestic Animal" - - log.parameters["race"] = race - - if(!istype(M, /mob/new_player) && M) - log.parameters["uspeech"] = M.universal_speak - else - log.parameters["uspeech"] = 0 - - // If the signal is still compressed, make the log entry gibberish - if(signal.data["compression"] > 0) - log.parameters["message"] = Gibberish(signal.data["message"], signal.data["compression"] + 50) - log.parameters["job"] = Gibberish(signal.data["job"], signal.data["compression"] + 50) - log.parameters["name"] = Gibberish(signal.data["name"], signal.data["compression"] + 50) - log.parameters["realname"] = Gibberish(signal.data["realname"], signal.data["compression"] + 50) - log.parameters["vname"] = Gibberish(signal.data["vname"], signal.data["compression"] + 50) - log.input_type = "Corrupt File" - - // Log and store everything that needs to be logged - log_entries.Add(log) - if(!(signal.data["name"] in stored_names)) - stored_names.Add(signal.data["name"]) - logs++ - signal.data["server"] = src - - // Give the log a name - var/identifier = num2text( rand(-1000,1000) + world.time ) - log.name = "data packet ([md5(identifier)])" - - var/can_send = relay_information(signal, "/obj/machinery/telecomms/hub") - if(!can_send) - relay_information(signal, "/obj/machinery/telecomms/broadcaster") - - -/obj/machinery/telecomms/server/proc/setcode(var/t) - if(t) - if(istext(t)) - rawcode = t - -/obj/machinery/telecomms/server/proc/update_logs() - // start deleting the very first log entry - if(logs >= 400) - for(var/i = 1, i <= logs, i++) // locate the first garbage collectable log entry and remove it - var/datum/comm_log_entry/L = log_entries[i] - if(L.garbage_collector) - log_entries.Remove(L) - logs-- - break - -/obj/machinery/telecomms/server/proc/add_entry(var/content, var/input) - var/datum/comm_log_entry/log = new - var/identifier = num2text( rand(-1000,1000) + world.time ) - log.name = "[input] ([md5(identifier)])" - log.input_type = input - log.parameters["message"] = content - log.parameters["timecode"] = stationtime2text() - log_entries.Add(log) - update_logs() - -// Simple log entry datum - -/datum/comm_log_entry - var/parameters = list() // carbon-copy to signal.data[] - var/name = "data packet (#)" - var/garbage_collector = 1 // if set to 0, will not be garbage collected - var/input_type = "Speech File" - -//Generic telecomm connectivity test proc -/proc/can_telecomm(var/atom/A, var/atom/B, var/ad_hoc = FALSE) - if(!A || !B) - log_debug(SPAN_DEBUG("can_telecomm(): Undefined endpoints!")) - return FALSE - - //Can't in this case, obviously! - if(is_jammed(A) || is_jammed(B)) - return FALSE - - //Items don't have a Z when inside an object or mob - var/turf/src_z = get_z(A) - var/turf/dst_z = get_z(B) - - //Nullspace, probably. - if(!src_z || !dst_z) - return FALSE - - //We can do the simple check first, if you have ad_hoc radios. - if(ad_hoc && src_z == dst_z) - return TRUE - - return src_z in GLOB.using_map.get_map_levels(dst_z, TRUE, om_range = DEFAULT_OVERMAP_RANGE) diff --git a/code/game/objects/items/circuitboards/computer/_computer.dm b/code/game/objects/items/circuitboards/computer/_computer.dm new file mode 100644 index 00000000000..03ed937eb4a --- /dev/null +++ b/code/game/objects/items/circuitboards/computer/_computer.dm @@ -0,0 +1,2 @@ +/obj/item/circuitboard/computer + board_type = new /datum/frame/frame_types/computer diff --git a/code/game/objects/items/circuitboards/computer/misc.dm b/code/game/objects/items/circuitboards/computer/misc.dm new file mode 100644 index 00000000000..705287419e6 --- /dev/null +++ b/code/game/objects/items/circuitboards/computer/misc.dm @@ -0,0 +1,3 @@ +/obj/item/circuitboard/computer/bioscan + name = T_BOARD("Bioscan Control Console") + build_path = /obj/machinery/computer/bioscan diff --git a/code/game/objects/items/circuitboards/machinery/_machine.dm b/code/game/objects/items/circuitboards/machinery/_machine.dm new file mode 100644 index 00000000000..03eabb04d16 --- /dev/null +++ b/code/game/objects/items/circuitboards/machinery/_machine.dm @@ -0,0 +1,2 @@ +/obj/item/circuitboard/machine + board_type = new /datum/frame/frame_types/machine diff --git a/code/game/objects/items/circuitboards/machinery/misc.dm b/code/game/objects/items/circuitboards/machinery/misc.dm new file mode 100644 index 00000000000..6516b43c1c1 --- /dev/null +++ b/code/game/objects/items/circuitboards/machinery/misc.dm @@ -0,0 +1,6 @@ +/obj/item/circuitboard/machine/bioscan + name = T_BOARD("Bioscan Antenna") + build_path = /obj/machinery/bioscan_antenna + req_components = list( + /obj/item/stock_parts/scanning_module = 1, + ) diff --git a/code/modules/automata/sonar.dm b/code/modules/automata/sonar.dm index 4cc44c7cd9c..0b16f942e6c 100644 --- a/code/modules/automata/sonar.dm +++ b/code/modules/automata/sonar.dm @@ -31,6 +31,12 @@ return ..() /datum/automata/wave/sonar/proc/flick_sonar(atom/movable/AM) + if(ismob(AM)) + var/mob/M = AM + if(M.client && !TIMER_COOLDOWN_CHECK(M, CD_INDEX_SONAR_NOISE)) + to_chat(M, SPAN_WARNING("You hear a quiet click.")) + TIMER_COOLDOWN_START(M, CD_INDEX_SONAR_NOISE, 7.5 SECONDS) + // todo: M.provoke() for AI... var/atom/movable/holder = AM.make_sonar_image(resolution) if(holder) holder.alpha = 0 diff --git a/code/modules/research/designs/circuits/circuit.dm b/code/modules/research/designs/circuits/circuit.dm new file mode 100644 index 00000000000..9c6d44a1bd9 --- /dev/null +++ b/code/modules/research/designs/circuits/circuit.dm @@ -0,0 +1,2 @@ +/datum/design/circuit + abstract_type = /datum/design/circuit diff --git a/code/modules/research/designs/circuits/computer.dm b/code/modules/research/designs/circuits/computer.dm new file mode 100644 index 00000000000..1374a55500b --- /dev/null +++ b/code/modules/research/designs/circuits/computer.dm @@ -0,0 +1,6 @@ +/datum/design/circuit/computer + abstract_type = /datum/design/circuit/computer + +/datum/design/circuit/computer/AssembleDesignName() + name = "Computer circuit design ([name])" + diff --git a/code/modules/research/designs/circuits/computers/misc.dm b/code/modules/research/designs/circuits/computers/misc.dm new file mode 100644 index 00000000000..ac50d9afabd --- /dev/null +++ b/code/modules/research/designs/circuits/computers/misc.dm @@ -0,0 +1,5 @@ +/datum/design/circuit/computer/bioscan_console + name = "Bioscan Console" + build_path = /obj/item/circuitboard/computer/bioscan + id = "console_bioscan" + req_tech = list(TECH_DATA = 1) diff --git a/code/modules/research/designs/circuits/machine.dm b/code/modules/research/designs/circuits/machine.dm new file mode 100644 index 00000000000..8ac273cc4f8 --- /dev/null +++ b/code/modules/research/designs/circuits/machine.dm @@ -0,0 +1,5 @@ +/datum/design/circuit/machine + abstract_type = /datum/design/circuit/machine + +/datum/design/circuit/machine/AssembleDesignName() + name = "Machine circuit design ([name])" diff --git a/code/modules/research/designs/circuits/machines/misc.dm b/code/modules/research/designs/circuits/machines/misc.dm new file mode 100644 index 00000000000..944687ff1dc --- /dev/null +++ b/code/modules/research/designs/circuits/machines/misc.dm @@ -0,0 +1,5 @@ +/datum/design/circuit/machine/bioscan_antenna + name = "Bioscan Antenna" + build_path = /obj/item/circuitboard/machine/bioscan + id = "machine_bioscan" + req_tech = list(TECH_DATA = 1) diff --git a/code/modules/tgui/tgui_input_text.dm b/code/modules/tgui/tgui_input_text.dm index f506c6d360a..d4a5290bb41 100644 --- a/code/modules/tgui/tgui_input_text.dm +++ b/code/modules/tgui/tgui_input_text.dm @@ -1,3 +1,21 @@ +/** + * Default operation for getting text input from a user. + * + * This can either route to TGUI or to native BYOND input depending on user prefs. + * + * @params + * * user - The user to show the text input to. + * * message - The content of the text input, shown in the body of the TGUI window. + * * title - The title of the text input modal, shown on the top of the TGUI window. + * * default - The default (or current) value, shown as a placeholder. + * * max_length - Specifies a max length for input. MAX_MESSAGE_LEN is default (1024) + * * multiline - Bool that determines if the input box is much larger. Good for large messages, laws, etc. + * * encode - Toggling this determines if input is filtered via html_encode. Setting this to FALSE gives raw input. + * * timeout - The timeout of the textbox, after which the modal will close and qdel itself. Set to zero for no timeout. + */ +/proc/default_input_text(mob/user, message = "", title = "Text Input", default, max_length = MAX_MESSAGE_LEN, multiline = FALSE, encode = TRUE, timeout = 0) + return input(user, message, title, default) as text|null + /** * Creates a TGUI window with a text input. Returns the user's response. * diff --git a/icons/README.md b/icons/README.md index a7d190a867f..b5ba6b542f3 100644 --- a/icons/README.md +++ b/icons/README.md @@ -19,6 +19,7 @@ Yes, this currently includes all turfs, mobs, objs, and misc things. Sorry. We'l - /job - basic job uniforms - /misc - unsorted things - /rank - "formal" "rank"-wear like flight suits, military/naval, etc + - /machinery - used for sprites for general, non-module machines. use subdirectories if more than one .dmi is needed. - /mapping - used for sprites for in-map-editor objects that are invisible in game world - /helpers - mapping helpers like autopipe/autocable/baseturf replacers go here - /landmarks - landmark icons diff --git a/icons/machinery/bioscan.dmi b/icons/machinery/bioscan.dmi new file mode 100644 index 00000000000..e0acd178a31 Binary files /dev/null and b/icons/machinery/bioscan.dmi differ diff --git a/tgui/package.json b/tgui/package.json index 3fb2d7f0cb0..9d4ca4a5d6b 100644 --- a/tgui/package.json +++ b/tgui/package.json @@ -42,6 +42,7 @@ "eslint-plugin-react": "^7.24.0", "eslint-plugin-unused-imports": "^1.1.4", "file-loader": "^6.2.0", + "ignore-loader": "^0.1.2", "inferno": "^7.4.8", "jest": "^27.0.6", "jest-circus": "^27.0.6", diff --git a/tgui/packages/tgui/backend.ts b/tgui/packages/tgui/backend.ts index 4fd6ae5f7e5..0c34690abdd 100644 --- a/tgui/packages/tgui/backend.ts +++ b/tgui/packages/tgui/backend.ts @@ -246,7 +246,7 @@ type BackendState = { title: string, status: number, interface: string, - refreshing: boolean, + refreshing: number, window: { key: string, size: [number, number], diff --git a/tgui/packages/tgui/components/Collapsible.js b/tgui/packages/tgui/components/Collapsible.tsx similarity index 68% rename from tgui/packages/tgui/components/Collapsible.js rename to tgui/packages/tgui/components/Collapsible.tsx index 8b915814ba4..548bf21663a 100644 --- a/tgui/packages/tgui/components/Collapsible.js +++ b/tgui/packages/tgui/components/Collapsible.tsx @@ -4,16 +4,32 @@ * @license MIT */ -import { Component } from 'inferno'; -import { Box } from './Box'; +import { BooleanLike } from 'common/react'; +import { Component, InfernoNode } from 'inferno'; +import { Box, BoxProps } from './Box'; import { Button } from './Button'; -export class Collapsible extends Component { +interface CollapsibleProps extends BoxProps { + buttons?: InfernoNode; + color?: string; + title?: string; + open?: BooleanLike; +} + +interface CollapsibleState { + open: boolean; +} + +export class Collapsible extends Component { + state: CollapsibleState = { + open: false, + } + constructor(props) { super(props); const { open } = props; this.state = { - open: open || false, + open: !!open || false, }; } diff --git a/tgui/packages/tgui/interfaces/README.md b/tgui/packages/tgui/interfaces/README.md new file mode 100644 index 00000000000..7ef39b7a896 --- /dev/null +++ b/tgui/packages/tgui/interfaces/README.md @@ -0,0 +1,36 @@ +# Interfaces Folder + +TGUI interfaces go in here. + +## Interface Routing + +The following paths are routed: +- ./ (for everything else) +. ./computers (for computers and consoles) +- ./items (for items) +- ./machines (for machinery for any kind) +- ./modules (for TGUI modules) + +This means that we **only** search for interfaces in these paths. `routes.js` controls this routing. + +Interface routing uses the following paths: +- {dir}/{name}.js +- {dir}/{name}.tsx +- {dir}/{name}/index.js +- {dir}/{name}/index.tsx + +This means your interface must match one of these, *and* export the {name} in an `export const` for this to work! + +## Interface Prefixes + +Interfaces are resolved by name only. Two interfaces in different folders with the same name will collide. +Hence, prefixes are enforced for the following folders: + +- ./modules interfaces should begin with `TGUI`, e.g. `TGUICardMod` +- ./ui interfaces should begin with `UI`, e.g. `UIListPicker` + +## Common Interfaces + +Common interfaces that are meant to be *potentially* standalone can be a /datum/tgui_module, whose interfaces generally goes in ./modules. + +Common interfaces that are not meant to be standalone go in ./common. See ./common/Access.tsx for an example. diff --git a/tgui/packages/tgui/interfaces/TelecommsLogBrowser.js b/tgui/packages/tgui/interfaces/TelecommsLogBrowser.tsx similarity index 61% rename from tgui/packages/tgui/interfaces/TelecommsLogBrowser.js rename to tgui/packages/tgui/interfaces/TelecommsLogBrowser.tsx index 148d8941de7..b64f5933aa6 100644 --- a/tgui/packages/tgui/interfaces/TelecommsLogBrowser.js +++ b/tgui/packages/tgui/interfaces/TelecommsLogBrowser.tsx @@ -1,19 +1,56 @@ import { round } from 'common/math'; +import { BooleanLike } from 'common/react'; import { Fragment } from 'inferno'; import { useBackend } from "../backend"; -import { Box, Button, Flex, NoticeBox, LabeledList, Section } from "../components"; +import { Box, Button, Flex, NoticeBox, LabeledList, Section, Collapsible } from "../components"; +import { formatTime } from '../format'; import { Window } from "../layouts"; -export const TelecommsLogBrowser = (props, context) => { - const { act, data } = useBackend(context); +interface TelecommsLogBrowserContext { + universal_translate: BooleanLike; + network: string; + temp: UITemporaryMessage; + servers: [TelecommsServer]; + selectedServer: TelecommsServerSelected; +} - const { - universal_translate, - network, - temp, - servers, - selectedServer, - } = data; +interface TelecommsServer { + id: string; + name: string; +} + +interface TelecommsServerSelected { + id: string; + totalTraffic: number; + logs: [MessageLog]; + triangulating: BooleanLike; + triangulation: [TriangulationEntry]; +} + +interface TriangulationEntry { + name: string; + x: number; + y: number; + z: string; + accuracy: number; + last: number; + tag: string; +} + +interface MessageLog { + name: string; + input_type: string; + id: number; + parameters: [Record]; +} + +interface UITemporaryMessage { + color: string; + text: string; +} + +export const TelecommsLogBrowser = (props, context) => { + const { act, data } = useBackend(context); return ( { height={450} resizable> - {(temp && temp.length) ? ( + {!!data.temp && ( - {temp} + {data.temp.text}