diff --git a/code/__DEFINES/jamming_defines.dm b/code/__DEFINES/jamming_defines.dm
index d3644e74d67f8..51fb5fb1c752e 100644
--- a/code/__DEFINES/jamming_defines.dm
+++ b/code/__DEFINES/jamming_defines.dm
@@ -1,8 +1,10 @@
//Radio jammer levels
//They will jam any signals that have the same or lower protection value
+#define JAMMER_LEVEL_NONE -1 //Jams nothing
#define RADIO_JAMMER_ABDUCTOR_LEVEL 1 //Power of the abductor radio jammer
#define RADIO_JAMMER_TRAITOR_LEVEL 3 //Power of the traitor radio jammer
+#define RADIO_JAMMER_MAINTENANCE_LEVEL 0 // Maintenance jams some signals with minor jamming
//Radio jammer protection level
#define JAMMER_PROTECTION_RADIO_BASIC 0 //Basic comms channels
@@ -12,3 +14,12 @@
#define JAMMER_PROTECTION_WIRELESS 1 //Wireless networking
#define JAMMER_PROTECTION_AI_SHELL 2 //AI shell protection
#define JAMMER_PROTECTION_SILICON_COMMS 1 //Silicon comms
+#define JAMMER_PROTECTION_PDA_MESSAGE 0 //PDA messages
+
+// Signal jamming
+/// Not jammed
+#define JAM_NONE 0
+/// Some jamming effects, but not completely disabling the device
+#define JAM_MINOR 1
+/// Fully jammed, cannot function
+#define JAM_FULL 2
diff --git a/code/datums/components/jam_receiver.dm b/code/datums/components/jam_receiver.dm
index 857f32eef59e5..97a37c4f227e4 100644
--- a/code/datums/components/jam_receiver.dm
+++ b/code/datums/components/jam_receiver.dm
@@ -63,7 +63,7 @@
/datum/component/jam_receiver/proc/check_jammed()
var/atom/atom_parent = parent
- var/new_state = atom_parent.is_jammed(intensity_resist)
+ var/new_state = atom_parent.is_jammed(intensity_resist) == JAM_FULL
set_jammed(new_state)
/datum/component/jam_receiver/proc/set_jammed(new_state)
diff --git a/code/game/area/Space_Station_13_areas.dm b/code/game/area/Space_Station_13_areas.dm
index 39db3eb4bdb85..d31eee7ff7f3e 100644
--- a/code/game/area/Space_Station_13_areas.dm
+++ b/code/game/area/Space_Station_13_areas.dm
@@ -165,7 +165,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
min_ambience_cooldown = 20 SECONDS
max_ambience_cooldown = 35 SECONDS
sound_environment = SOUND_AREA_TUNNEL_ENCLOSED
- area_flags = BLOBS_ALLOWED | UNIQUE_AREA
mood_bonus = -1
mood_message = "It's kind of cramped in here!\n"
// assistants are associated with maints, jani closet is in maints, engis have to go into maints often
@@ -177,6 +176,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
lights_always_start_on = TRUE
color_correction = /datum/client_colour/area_color/cold_ish
camera_networks = list(CAMERA_NETWORK_STATION)
+ area_jamming_level = RADIO_JAMMER_MAINTENANCE_LEVEL
/area/maintenance/get_area_textures()
return GLOB.turf_texture_maint
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 8afb44fbd6704..3fac44659aaa1 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -140,6 +140,8 @@
/// What networks should cameras in this area belong to?
var/list/camera_networks = list()
+ var/area_jamming_level = JAMMER_LEVEL_NONE
+
/**
* A list of teleport locations
*
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 67f098cb93210..d4f9be5b1fd72 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -497,7 +497,7 @@ CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/camera)
return FALSE
if(machine_stat & EMPED)
return FALSE
- if(is_jammed(JAMMER_PROTECTION_CAMERAS))
+ if(is_jammed(JAMMER_PROTECTION_CAMERAS) == JAM_FULL)
return FALSE
return TRUE
diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm
index 838c200652cba..ce470ceea0032 100644
--- a/code/game/machinery/camera/tracking.dm
+++ b/code/game/machinery/camera/tracking.dm
@@ -99,7 +99,7 @@
track_time -= 1 SECONDS
// Check for sensor beacon
var/nanite_sensors = HAS_TRAIT(human_target, TRAIT_SUIT_SENSORS)
- if(!human_target.is_jammed(JAMMER_PROTECTION_SENSOR_NETWORK) && (nanite_sensors || HAS_TRAIT(human_target, TRAIT_NANITE_SENSORS)))
+ if(target.is_jammed(JAMMER_PROTECTION_SENSOR_NETWORK) == JAM_NONE && (nanite_sensors || HAS_TRAIT(human_target, TRAIT_NANITE_SENSORS)))
// Check for a uniform if not using nanites
// If the GPS is on, track instantly
var/obj/item/clothing/under/uniform = human_target.w_uniform
diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm
index c44799a9d85ae..3647f7f28369a 100644
--- a/code/game/machinery/computer/crew.dm
+++ b/code/game/machinery/computer/crew.dm
@@ -252,7 +252,8 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
continue
// Radio transmitters are jammed
- if(tracked_human.is_jammed(JAMMER_PROTECTION_SENSOR_NETWORK))
+ var/jam_state = tracked_human.is_jammed(JAMMER_PROTECTION_SENSOR_NETWORK)
+ if(jam_state == JAM_FULL)
continue
// The entry for this human
@@ -271,19 +272,19 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
entry["ijob"] = jobs[I.hud_state]
// Binary living/dead status
- if (nanite_sensors || uniform.sensor_mode >= SENSOR_LIVING)
+ if ((nanite_sensors || uniform.sensor_mode >= SENSOR_LIVING))
entry["life_status"] = !tracked_human.stat
// Damage
- if (nanite_sensors || uniform.sensor_mode >= SENSOR_VITALS)
- entry["oxydam"] = round(tracked_human.getOxyLoss(), 1)
- entry["toxdam"] = round(tracked_human.getToxLoss(), 1)
- entry["burndam"] = round(tracked_human.getFireLoss(), 1)
- entry["brutedam"] = round(tracked_human.getBruteLoss(), 1)
+ if ((nanite_sensors || uniform.sensor_mode >= SENSOR_VITALS))
+ entry["oxydam"] = round(tracked_human.getOxyLoss(), 1) + (jam_state != JAM_NONE && rand(-5, 5))
+ entry["toxdam"] = round(tracked_human.getToxLoss(), 1) + (jam_state != JAM_NONE && rand(-5, 5))
+ entry["burndam"] = round(tracked_human.getFireLoss(), 1) + (jam_state != JAM_NONE && rand(-5, 5))
+ entry["brutedam"] = round(tracked_human.getBruteLoss(), 1) + (jam_state != JAM_NONE && rand(-5, 5))
// Area
if (pos && (nanite_sensors || uniform.sensor_mode >= SENSOR_COORDS))
- entry["area"] = get_area_name(tracked_human, TRUE)
+ entry["area"] = jam_state == JAM_NONE ? get_area_name(tracked_human, TRUE) : scramble_message_replace_chars(get_area_name(tracked_human, TRUE), 80)
// Trackability
entry["can_track"] = tracked_human.can_track()
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 3aeb15a6cc0da..8a26d17142908 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -246,7 +246,7 @@
return
//Check radio signal jamming
- if(is_jammed(JAMMER_PROTECTION_WIRELESS))
+ if(is_jammed(JAMMER_PROTECTION_WIRELESS) == JAM_FULL)
return
@@ -455,14 +455,14 @@
/obj/machinery/door/airlock/proc/canAIControl(mob/user)
if(protected_door)
return FALSE
- if(is_jammed(JAMMER_PROTECTION_WIRELESS))
+ if(is_jammed(JAMMER_PROTECTION_WIRELESS) == JAM_FULL)
return FALSE
return ((aiControlDisabled != 1) && !isAllPowerCut())
/obj/machinery/door/airlock/proc/canAIHack()
if(protected_door)
return FALSE
- if(is_jammed(JAMMER_PROTECTION_WIRELESS))
+ if(is_jammed(JAMMER_PROTECTION_WIRELESS) == JAM_FULL)
return FALSE
return ((aiControlDisabled==1) && (!hackProof) && (!isAllPowerCut()));
@@ -779,7 +779,7 @@
if(detonated)
to_chat(user, "Unable to interface. Airlock control panel damaged.")
return
- if(is_jammed(JAMMER_PROTECTION_WIRELESS))
+ if(is_jammed(JAMMER_PROTECTION_WIRELESS) == JAM_FULL)
to_chat(user, "Unable to interface. Remote communications not responding.")
return
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index a4fe805c76b50..108b64fe066af 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -385,7 +385,7 @@ CREATION_TEST_IGNORE_SUBTYPES(/obj/machinery/door/window)
return
//Check radio signal jamming
- if(is_jammed(JAMMER_PROTECTION_WIRELESS))
+ if(is_jammed(JAMMER_PROTECTION_WIRELESS) == JAM_FULL)
return
// Check packet access level.
diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm
index 14f3005bc80b3..0db80db698403 100644
--- a/code/game/machinery/telecomms/telecomunications.dm
+++ b/code/game/machinery/telecomms/telecomunications.dm
@@ -177,7 +177,7 @@ GLOBAL_LIST_EMPTY(telecomms_list)
/obj/machinery/telecomms/proc/ntnet_receive(datum/source, datum/netdata/data)
//Check radio signal jamming
- if(is_jammed(JAMMER_PROTECTION_WIRELESS) || machine_stat & (BROKEN|NOPOWER|MAINT|EMPED))
+ if(is_jammed(JAMMER_PROTECTION_WIRELESS) == JAM_FULL || machine_stat & (BROKEN|NOPOWER|MAINT|EMPED))
return
switch(data.data["type"])
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 7728983671528..b4f87139824ac 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -299,8 +299,11 @@
channel = null
// Nearby active jammers prevent the message from transmitting
- if(is_jammed(freq == FREQ_CENTCOM || freq == FREQ_SYNDICATE ? JAMMER_PROTECTION_RADIO_ADVANCED : JAMMER_PROTECTION_RADIO_BASIC))
+ var/jam_level = is_jammed(freq == FREQ_CENTCOM || freq == FREQ_SYNDICATE ? JAMMER_PROTECTION_RADIO_ADVANCED : JAMMER_PROTECTION_RADIO_BASIC)
+ if(jam_level == JAM_FULL)
return
+ if (jam_level == JAM_MINOR)
+ message = Gibberish(message, TRUE)
// Determine the identity information which will be attached to the signal.
var/atom/movable/virtualspeaker/speaker = new(null, talking_movable, src)
@@ -369,7 +372,7 @@
if (input_frequency == FREQ_SYNDICATE && !syndie)
return FALSE
-
+
// allow checks: are we listening on that frequency?
if (input_frequency == frequency)
return TRUE
diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm
index d43ae7b6a2829..18093453e3d84 100644
--- a/code/game/objects/items/devices/traitordevices.dm
+++ b/code/game/objects/items/devices/traitordevices.dm
@@ -290,12 +290,15 @@ effective or pretty fucking useless.
///Parameters:
/// - Protection level: The amount of protection that the atom has. See jamming_defines.dm
/atom/proc/is_jammed(protection_level)
+ . = JAM_NONE
var/turf/position = get_turf(src)
+ var/area/location = position.loc
+ if (protection_level <= location.area_jamming_level)
+ . = JAM_MINOR
for(var/datum/component/radio_jamming/jammer as anything in GLOB.active_jammers)
//Check to see if the jammer is strong enough to block this signal
if (protection_level > jammer.intensity)
continue
var/turf/jammer_turf = get_turf(jammer.parent)
if(position?.get_virtual_z_level() == jammer_turf.get_virtual_z_level() && (get_dist(position, jammer_turf) <= jammer.range))
- return TRUE
- return FALSE
+ return JAM_FULL
diff --git a/code/game/objects/items/pinpointer.dm b/code/game/objects/items/pinpointer.dm
index a2e9704c772d4..4832c3da6466c 100644
--- a/code/game/objects/items/pinpointer.dm
+++ b/code/game/objects/items/pinpointer.dm
@@ -80,7 +80,7 @@
. = ..()
if(!active)
return
- if(!target || (!isnull(jamming_resistance) && src.is_jammed(jamming_resistance)))
+ if(!target || (!isnull(jamming_resistance) && src.is_jammed(jamming_resistance) != JAM_NONE))
. += "pinon[alert ? "alert" : ""]null[icon_suffix]"
return
var/turf/here = get_turf(src)
@@ -118,7 +118,7 @@
z_level_direction = ""
// getting xy result
- if(get_dist_euclidian(here,there) <= minimum_range)
+ if(get_dist_euclidian(here,there) <= minimum_range || (target.is_jammed(jamming_resistance) != JAM_NONE) && get_dist_euclidian(here,there) < minimum_range + 25)
pin_xy_result = "direct"
else
setDir(get_dir(here, there))
@@ -163,7 +163,7 @@
if(!here || !there)
return FALSE
- if(there.is_jammed(jam_level))
+ if(there.is_jammed(jam_level) == JAM_FULL)
return FALSE
if(!powerful_z_check) // z-check will be only limited within the same area (i.e. multi-floor'ed station)
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index d5ac9e90095f7..ff82790861189 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -330,7 +330,7 @@ CREATION_TEST_IGNORE_SUBTYPES(/mob/living/silicon/ai)
if ((ai.get_virtual_z_level() != target.get_virtual_z_level()) && !is_station_level(ai.z))
return FALSE
- if(A.is_jammed(JAMMER_PROTECTION_WIRELESS))
+ if(A.is_jammed(JAMMER_PROTECTION_WIRELESS) == JAM_FULL)
return FALSE
if (istype(loc, /obj/item/aicard))
@@ -994,7 +994,7 @@ CREATION_TEST_IGNORE_SUBTYPES(/mob/living/silicon/ai)
if (!target || target.stat || target.deployed || !(!target.connected_ai ||(target.connected_ai == src)) || (target.ratvar && !is_servant_of_ratvar(src)))
return
- if(target.is_jammed(JAMMER_PROTECTION_AI_SHELL))
+ if(target.is_jammed(JAMMER_PROTECTION_AI_SHELL) == JAM_FULL)
to_chat(src, "Unable to establish communication link with target.")
return
diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm
index 193438412356a..6c06ee77d1212 100644
--- a/code/modules/mob/living/silicon/robot/life.dm
+++ b/code/modules/mob/living/silicon/robot/life.dm
@@ -8,7 +8,7 @@
handle_robot_cell()
/mob/living/silicon/robot/proc/handle_jamming()
- if(deployed && is_jammed(JAMMER_PROTECTION_AI_SHELL))
+ if(deployed && is_jammed(JAMMER_PROTECTION_AI_SHELL) == JAM_FULL)
to_chat(src, "Remote connection with target lost.")
undeploy()
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 659ad5d21bac9..5fb9fd6181dac 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -375,7 +375,7 @@
var/turf/T1 = get_turf(A)
if (!T0 || ! T1)
return FALSE
- if(A.is_jammed(JAMMER_PROTECTION_WIRELESS))
+ if(A.is_jammed(JAMMER_PROTECTION_WIRELESS) == JAM_FULL)
return FALSE
return ISINRANGE(T1.x, T0.x - interaction_range, T0.x + interaction_range) && ISINRANGE(T1.y, T0.y - interaction_range, T0.y + interaction_range)
diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm
index 1a0797243f43c..ad7e7749e3885 100644
--- a/code/modules/mob/living/silicon/say.dm
+++ b/code/modules/mob/living/silicon/say.dm
@@ -1,6 +1,6 @@
/mob/living/proc/robot_talk(message)
//Cannot transmit wireless messages while jammed
- if(is_jammed(JAMMER_PROTECTION_SILICON_COMMS))
+ if(is_jammed(JAMMER_PROTECTION_SILICON_COMMS) == JAM_FULL)
return
if(CHAT_FILTER_CHECK(message))
to_chat(usr, "Your message contains forbidden words.")
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index 57c63fa117ab6..3f0d2259964a3 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -1080,7 +1080,7 @@ Pass a positive integer as an argument to override a bot's default speed.
/mob/living/simple_animal/bot/proc/topic_denied(mob/user) //Access check proc for bot topics! Remember to place in a bot's individual Topic if desired.
//Silicons cannot remotely interfact with robots while the robot is jammed
- if(issilicon(user) && is_jammed(JAMMER_PROTECTION_WIRELESS))
+ if(issilicon(user) && is_jammed(JAMMER_PROTECTION_WIRELESS) == JAM_FULL)
return TRUE
if(!user.canUseTopic(src, !issilicon(user)))
return TRUE
diff --git a/code/modules/modular_computers/file_system/programs/ntmessenger.dm b/code/modules/modular_computers/file_system/programs/ntmessenger.dm
index f24fc7ffbf08e..1c19cd1262b14 100644
--- a/code/modules/modular_computers/file_system/programs/ntmessenger.dm
+++ b/code/modules/modular_computers/file_system/programs/ntmessenger.dm
@@ -292,11 +292,17 @@
return
// Check for jammers
- var/turf/position = get_turf(computer)
- for(var/datum/component/radio_jamming/jammer as anything in GLOB.active_jammers)
- var/turf/jammer_turf = get_turf(jammer.parent)
- if(position?.get_virtual_z_level() == jammer_turf.get_virtual_z_level() && (get_dist(position, jammer_turf) <= jammer.range))
- return FALSE
+ var/jam_level = computer.is_jammed(JAMMER_PROTECTION_PDA_MESSAGE)
+
+ if (jam_level == JAM_FULL)
+ to_chat(user, "ERROR: Server isn't responding.")
+ if(ringer_status)
+ playsound(computer, 'sound/machines/terminal_error.ogg', 15, TRUE)
+ return
+
+ if (jam_level == JAM_MINOR)
+ // Slightly more robust than comms, not quite as gibberish
+ message = Gibberish(message, TRUE, 30)
// Send the signal
var/list/string_targets = list()