diff --git a/code/__DEFINES/mob_hud.dm b/code/__DEFINES/mob_hud.dm
index a39160cc2247..1b9cf6120535 100644
--- a/code/__DEFINES/mob_hud.dm
+++ b/code/__DEFINES/mob_hud.dm
@@ -13,8 +13,6 @@
#define XENO_HOSTILE_SLOW "13" // xeno-inflicted slow. used by a bunch of MOBA xenos stuff
#define XENO_HOSTILE_TAG "14" // dancer prae 'tag'
#define XENO_HOSTILE_FREEZE "15" // Any xeno-inflifcted root
-#define XENO_EXECUTE "28" // Execute thershold, vampire
-
#define HEALTH_HUD_XENO "16" // health HUD for xenos
#define PLASMA_HUD "17" // indicates the plasma level of xenos.
#define PHEROMONE_HUD "18" // indicates which pheromone is active on a xeno.
@@ -27,7 +25,9 @@
#define HUNTER_CLAN "25" //Displays a colored icon to represent ingame Hunter Clans
#define HUNTER_HUD "26" //Displays various statuses on mobs for Hunters to identify targets
#define HOLOCARD_HUD "27" //Displays the holocards set by medical personnel
+#define XENO_EXECUTE "28" // Execute thershold, vampire
#define NEW_PLAYER_HUD "29" //Makes it easy to see new players.
+#define SPYCAM_HUD "30" //Remote control spy cameras.
//data HUD (medhud, sechud) defines
#define MOB_HUD_SECURITY_BASIC 1
@@ -49,6 +49,7 @@
#define MOB_HUD_HUNTER_CLAN 17
#define MOB_HUD_EXECUTE 18
#define MOB_HUD_NEW_PLAYER 19
+#define MOB_HUD_SPYCAMS 20
//for SL/FTL/LZ targeting on locator huds
#define TRACKER_SL "track_sl"
diff --git a/code/__DEFINES/mode.dm b/code/__DEFINES/mode.dm
index 1bf5fd08fa38..8d8524cea81f 100644
--- a/code/__DEFINES/mode.dm
+++ b/code/__DEFINES/mode.dm
@@ -292,6 +292,11 @@ DEFINE_BITFIELD(whitelist_status, list(
#define FACTION_LIST_MARINE_UPP list(FACTION_MARINE, FACTION_UPP)
#define FACTION_LIST_MARINE_TWE list(FACTION_MARINE, FACTION_TWE)
#define FACTION_LIST_YAUTJA list(FACTION_YAUTJA)
+#define FACTION_LIST_COLONY list(FACTION_SURVIVOR, FACTION_COLONIST)
+#define FACTION_LIST_NEUTRAL list(FACTION_NEUTRAL)
+
+/// The list of factions loosely allied with the USCM
+#define FACTION_LIST_MARINE_FAXES list(FACTION_MARINE, FACTION_WY, FACTION_MARSHAL, FACTION_TWE)
// Xenomorphs
#define FACTION_PREDALIEN "Predalien"
diff --git a/code/__DEFINES/paperwork.dm b/code/__DEFINES/paperwork.dm
new file mode 100644
index 000000000000..65bd6503e73e
--- /dev/null
+++ b/code/__DEFINES/paperwork.dm
@@ -0,0 +1,18 @@
+#define PAPER_CATEGORY_USCM "USCM"
+#define PAPER_CATEGORY_USCM_HC "USCM HC"
+
+#define PAPER_CATEGORY_MP "Military Police"
+#define PAPER_CATEGORY_PROVOST "Provost"
+
+#define PAPER_CATEGORY_LIAISON "WY Liaison"
+#define PAPER_CATEGORY_WEYYU_HC "Weyland Yutani"
+
+#define PAPER_CATEGORY_UPP_HC "UPP HC"
+
+#define PAPER_CATEGORY_CMB_HC "CMB HC"
+
+#define PAPER_CATEGORY_CLF_HC "CLF HC"
+
+#define PAPER_CATEGORY_TWE_HC "TWE HC"
+
+#define PAPER_CATEGORY_PRESS_HC "PRESS HC"
diff --git a/code/__DEFINES/radio.dm b/code/__DEFINES/radio.dm
index 576e92ba79ff..433eca038d52 100644
--- a/code/__DEFINES/radio.dm
+++ b/code/__DEFINES/radio.dm
@@ -66,6 +66,11 @@
#define RADIO_CHANNEL_BUG_A "Listening Device A"
#define RADIO_CHANNEL_BUG_B "Listening Device B"
+//Fax Responder Bugs
+#define RADIO_CHANNEL_FAX_WY "WY Monitor"
+#define RADIO_CHANNEL_FAX_USCM_HC "USCM-HC Monitor"
+#define RADIO_CHANNEL_FAX_USCM_PVST "Provost Monitor"
+
//1-Channel ERTs
#define RADIO_CHANNEL_DUTCH_DOZEN "DD"
#define RADIO_CHANNEL_VAI "VAI"
diff --git a/code/_globalvars/lists/mapping_globals.dm b/code/_globalvars/lists/mapping_globals.dm
index 772561dbf4a6..67cf01d7e170 100644
--- a/code/_globalvars/lists/mapping_globals.dm
+++ b/code/_globalvars/lists/mapping_globals.dm
@@ -33,6 +33,7 @@ GLOBAL_LIST_EMPTY(zombie_landmarks)
GLOBAL_LIST_EMPTY(newplayer_start)
GLOBAL_LIST_EMPTY_TYPED(observer_starts, /obj/effect/landmark/observer_start)
+GLOBAL_LIST_EMPTY_TYPED(spycam_starts, /obj/effect/landmark/spycam_start)
GLOBAL_LIST_EMPTY(map_items)
GLOBAL_LIST_EMPTY(xeno_tunnels)
diff --git a/code/controllers/subsystem/communications.dm b/code/controllers/subsystem/communications.dm
index 1438c50ed986..06461ab18bc1 100644
--- a/code/controllers/subsystem/communications.dm
+++ b/code/controllers/subsystem/communications.dm
@@ -108,6 +108,11 @@ Radiochat range: 1441 to 1489 (most devices refuse to be tune to other frequency
#define BUG_A_FREQ 1290
#define BUG_B_FREQ 1291
+//Listening Bugs (1296-1300)
+#define FAX_WY_FREQ 1296
+#define FAX_USCM_HC_FREQ 1297
+#define FAX_USCM_PVST_FREQ 1298
+
//General Radio
#define MIN_FREQ 1460 // ------------------------------------------------------
#define PUB_FREQ 1461
@@ -212,6 +217,10 @@ GLOBAL_LIST_INIT(radiochannels, list(
RADIO_CHANNEL_BUG_A = BUG_A_FREQ,
RADIO_CHANNEL_BUG_B = BUG_B_FREQ,
+
+ RADIO_CHANNEL_FAX_WY = FAX_WY_FREQ,
+ RADIO_CHANNEL_FAX_USCM_HC = FAX_USCM_HC_FREQ,
+ RADIO_CHANNEL_FAX_USCM_PVST = FAX_USCM_PVST_FREQ,
))
// Response Teams
@@ -229,6 +238,9 @@ GLOBAL_LIST_INIT(radiochannels, list(
//Listening Device Frequencies
#define BUG_FREQS list(BUG_A_FREQ, BUG_B_FREQ)
+//Fax Responder internal monitor frequencies
+#define FAX_RESP_FREQS list(FAX_WY_FREQ, FAX_USCM_HC_FREQ, FAX_USCM_PVST_FREQ)
+
//Depts - used for colors in headset.dm, as well as deciding what the marine comms tower can listen into
#define DEPT_FREQS list(COMM_FREQ, MED_FREQ, ENG_FREQ, SEC_FREQ, SENTRY_FREQ, ALPHA_FREQ, BRAVO_FREQ, CHARLIE_FREQ, DELTA_FREQ, ECHO_FREQ, CRYO_FREQ, REQ_FREQ, JTAC_FREQ, INTEL_FREQ, WY_FREQ)
@@ -307,6 +319,9 @@ SUBSYSTEM_DEF(radio)
"[CLF_ENGI_FREQ]" = "opforeng",
"[CLF_MED_FREQ]" = "opformed",
"[CLF_CCT_FREQ]" = "opforcct",
+ "[FAX_WY_FREQ]" = "airadio",
+ "[FAX_USCM_HC_FREQ]" = "aiprivradio",
+ "[FAX_USCM_PVST_FREQ]" = "aiprivradio",
)
/datum/controller/subsystem/radio/proc/add_object(obj/device as obj, new_frequency as num, filter = null as text|null)
diff --git a/code/datums/mob_hud.dm b/code/datums/mob_hud.dm
index 7a8239615016..4404203576d1 100644
--- a/code/datums/mob_hud.dm
+++ b/code/datums/mob_hud.dm
@@ -21,6 +21,7 @@ GLOBAL_LIST_INIT_TYPED(huds, /datum/mob_hud, list(
MOB_HUD_HUNTER_CLAN = new /datum/mob_hud/hunter_clan(),
MOB_HUD_EXECUTE = new /datum/mob_hud/execute_hud(),
MOB_HUD_NEW_PLAYER = new /datum/mob_hud/new_player(),
+ MOB_HUD_SPYCAMS = new /datum/mob_hud/spy_cams(),
))
/datum/mob_hud
@@ -219,6 +220,9 @@ GLOBAL_LIST_INIT_TYPED(huds, /datum/mob_hud, list(
/datum/mob_hud/faction/observer
hud_icons = list(FACTION_HUD, ORDER_HUD, HUNTER_CLAN, HOLOCARD_HUD)
+/datum/mob_hud/spy_cams
+ hud_icons = list(SPYCAM_HUD)
+
///////// MOB PROCS //////////////////////////////:
diff --git a/code/datums/skills/civilian.dm b/code/datums/skills/civilian.dm
index b37fdad883c7..56dc638a4d32 100644
--- a/code/datums/skills/civilian.dm
+++ b/code/datums/skills/civilian.dm
@@ -250,8 +250,9 @@ CIVILIAN
SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
SKILL_LEADERSHIP = SKILL_LEAD_MASTER,
SKILL_OVERWATCH = SKILL_OVERWATCH_TRAINED,
- SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
- SKILL_ENGINEER = SKILL_ENGINEER_NOVICE,
+ SKILL_MEDICAL = SKILL_MEDICAL_MEDIC,
+ SKILL_ENGINEER = SKILL_ENGINEER_TRAINED,
SKILL_VEHICLE = SKILL_VEHICLE_SMALL,
SKILL_INTEL = SKILL_INTEL_EXPERT,
+ SKILL_POLICE = SKILL_POLICE_MAX,
)
diff --git a/code/game/area/admin_level.dm b/code/game/area/admin_level.dm
index f522ca24cd16..f66a225b2725 100644
--- a/code/game/area/admin_level.dm
+++ b/code/game/area/admin_level.dm
@@ -142,9 +142,18 @@
soundscape_playlist = SCAPE_PL_ELEVATOR_MUSIC
icon_state = "yellow"
+//Fax Responder areas
/area/adminlevel/ert_station/fax_response_station
name = "Sector Comms Relay"
icon_state = "green"
+ unlimited_power = TRUE
+
+/area/adminlevel/ert_station/fax_response_station/exterior
+ name = "Sector Comms Relay"
+ icon_state = "red"
+ ambience_exterior = AMBIENCE_JUNGLE
+ //ambience = list('sound/ambience/jungle_amb1.ogg')
+ base_lighting_alpha = 185
//Simulation area
/area/adminlevel/simulation
diff --git a/code/game/bioscans.dm b/code/game/bioscans.dm
index edd0b174212d..a37bf2838bdf 100644
--- a/code/game/bioscans.dm
+++ b/code/game/bioscans.dm
@@ -52,6 +52,8 @@ GLOBAL_DATUM_INIT(bioscan_data, /datum/bioscan_data, new)
for(var/mob/current_mob as anything in GLOB.living_xeno_list)
if(current_mob.mob_flags & NOBIOSCAN)
continue
+ if(istype(get_turf(current_mob), /area/adminlevel/ert_station/fax_response_station))
+ continue
var/area/A = get_area(current_mob)
if(A?.flags_area & AREA_AVOID_BIOSCAN)
xenos_on_ship++
@@ -70,6 +72,8 @@ GLOBAL_DATUM_INIT(bioscan_data, /datum/bioscan_data, new)
for(var/mob/living/carbon/human/current_human as anything in GLOB.alive_human_list)
if(current_human.mob_flags & NOBIOSCAN)
continue
+ if(istype(get_turf(current_human), /area/adminlevel/ert_station/fax_response_station))
+ continue
var/atom/where = current_human
if(isspecieshuman(current_human))
if (where.z == 0 && current_human.loc)
diff --git a/code/game/gamemodes/cm_initialize.dm b/code/game/gamemodes/cm_initialize.dm
index f4daf6e57bce..311ce590dfb3 100644
--- a/code/game/gamemodes/cm_initialize.dm
+++ b/code/game/gamemodes/cm_initialize.dm
@@ -321,9 +321,8 @@ Additional game mode variables.
log_debug("Null client attempted to transform_fax_responder")
return FALSE
if(!loaded_fax_base)
- loaded_fax_base = SSmapping.lazy_load_template(/datum/lazy_template/fax_response_base, force = TRUE)
+ load_fax_base()
if(!loaded_fax_base)
- log_debug("Error loading fax response base!")
return FALSE
responder_candidate.client.prefs.find_assigned_slot(JOB_FAX_RESPONDER)
@@ -344,11 +343,18 @@ Additional game mode variables.
GLOB.RoleAuthority.equip_role(new_responder, fax_responder_job, new_responder.loc)
SSticker.minds += new_responder.mind
- message_admins(FONT_SIZE_XL(SPAN_RED("([new_responder.key]) joined as a [sub_job], [new_responder.real_name].")))
+ message_admins(FONT_SIZE_XL(SPAN_RED("[key_name(new_responder)] joined as a [sub_job].")))
new_responder.add_fax_responder()
return TRUE
+/datum/game_mode/proc/load_fax_base()
+ loaded_fax_base = SSmapping.lazy_load_template(/datum/lazy_template/fax_response_base, force = TRUE)
+ if(!loaded_fax_base)
+ log_debug("Error loading fax response base!")
+ return FALSE
+ return TRUE
+
//===================================================\\
diff --git a/code/game/gamemodes/cm_process.dm b/code/game/gamemodes/cm_process.dm
index 2c11af9ca6f9..7dc31d6e55da 100644
--- a/code/game/gamemodes/cm_process.dm
+++ b/code/game/gamemodes/cm_process.dm
@@ -186,7 +186,7 @@ GLOBAL_VAR_INIT(next_admin_bioscan, 30 MINUTES)
var/num_xenos = 0
for(var/i in GLOB.living_xeno_list)
var/mob/M = i
- if(M.z && (M.z in z_levels) && !istype(M.loc, /turf/open/space)) //If they have a z var, they are on a turf.
+ if(M.z && (M.z in z_levels) && !istype(M.loc, /turf/open/space) && !istype(M.loc, /area/adminlevel/ert_station/fax_response_station)) //If they have a z var, they are on a turf.
num_xenos++
return num_xenos
@@ -195,7 +195,7 @@ GLOBAL_VAR_INIT(next_admin_bioscan, 30 MINUTES)
var/num_xenos = 0
for(var/mob/M in GLOB.player_list)
- if(M.z && (M.z in z_levels) && M.stat != DEAD && !istype(M.loc, /turf/open/space)) //If they have a z var, they are on a turf.
+ if(M.z && (M.z in z_levels) && M.stat != DEAD && !istype(M.loc, /turf/open/space) && !istype(M.loc, /area/adminlevel/ert_station/fax_response_station)) //If they have a z var, they are on a turf.
if(ishuman(M) && !isyautja(M) && !(M.status_flags & XENO_HOST) && !iszombie(M))
var/mob/living/carbon/human/H = M
if(((H.species && H.species.name == "Human") || (H.is_important)) && !H.hivenumber) //only real humans count, or those we have set to also be included
@@ -225,7 +225,7 @@ GLOBAL_VAR_INIT(next_admin_bioscan, 30 MINUTES)
for(var/i in GLOB.alive_human_list)
var/mob/M = i
- if(M.z && (M.z in z_levels) && !istype(M.loc, /turf/open/space))
+ if(M.z && (M.z in z_levels) && !istype(M.loc, /turf/open/space) && !istype(M.loc, /area/adminlevel/ert_station/fax_response_station))
if(M.faction in FACTION_LIST_WY)
num_pmcs++
else if(M.faction == FACTION_MARINE)
@@ -238,7 +238,7 @@ GLOBAL_VAR_INIT(next_admin_bioscan, 30 MINUTES)
for(var/i in GLOB.alive_human_list)
var/mob/M = i
- if(M.z && (M.z in z_levels) && !istype(M.loc, /turf/open/space))
+ if(M.z && (M.z in z_levels) && !istype(M.loc, /turf/open/space) && !istype(M.loc, /area/adminlevel/ert_station/fax_response_station))
if(M.faction == FACTION_MARINE)
num_marines++
@@ -252,7 +252,7 @@ GLOBAL_VAR_INIT(next_admin_bioscan, 30 MINUTES)
var/num_headcount = 0
for(var/mob/living/carbon/human/current_human as anything in GLOB.alive_human_list)
- if(!(current_human.z && (current_human.z in z_levels) && !istype(current_human.loc, /turf/open/space)))
+ if(!(current_human.z && (current_human.z in z_levels) && !istype(current_human.loc, /turf/open/space) && !istype(current_human.loc, /area/adminlevel/ert_station/fax_response_station)))
continue
if((current_human.faction in FACTION_LIST_WY) || current_human.job == "Corporate Liaison") //The CL is assigned the USCM faction for gameplay purposes
num_WY++
diff --git a/code/game/gamemodes/colonialmarines/xenovsxeno.dm b/code/game/gamemodes/colonialmarines/xenovsxeno.dm
index 2a7b29cea5b0..47ebd76d1b84 100644
--- a/code/game/gamemodes/colonialmarines/xenovsxeno.dm
+++ b/code/game/gamemodes/colonialmarines/xenovsxeno.dm
@@ -205,7 +205,7 @@
hivenumbers += list(HS.name = list())
for(var/mob/M in GLOB.player_list)
- if(M.z && (M.z in z_levels) && M.stat != DEAD && !istype(M.loc, /turf/open/space)) //If they have a z var, they are on a turf.
+ if(M.z && (M.z in z_levels) && M.stat != DEAD && !istype(M.loc, /turf/open/space) && !istype(M.loc, /area/adminlevel/ert_station/fax_response_station)) //If they have a z var, they are on a turf.
var/mob/living/carbon/xenomorph/X = M
var/datum/hive_status/hive = GLOB.hive_datum[X.hivenumber]
if(!hive)
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 1b566dcd3717..8ac1a9590585 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -38,6 +38,8 @@
var/autoname = FALSE
var/autonumber = 0 //camera number in area
+ var/list/owner_factions = FACTION_LIST_NEUTRAL
+
GLOBAL_LIST_EMPTY_TYPED(all_cameras, /obj/structure/machinery/camera)
/obj/structure/machinery/camera/Initialize(mapload, ...)
. = ..()
@@ -314,6 +316,9 @@ GLOBAL_LIST_EMPTY_TYPED(all_cameras, /obj/structure/machinery/camera)
linked_broadcasting = camera_item
c_tag = linked_broadcasting.get_broadcast_name()
+/obj/structure/machinery/camera/overwatch
+ network = list(CAMERA_NET_OVERWATCH)
+
/obj/structure/machinery/camera/mortar
alpha = 0
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm
index 6ab2d9569ab9..efe61436e1f4 100644
--- a/code/game/machinery/camera/presets.dm
+++ b/code/game/machinery/camera/presets.dm
@@ -59,6 +59,7 @@
icon = 'icons/obj/vehicles/interiors/general.dmi'
icon_state = "vehicle_camera"
network = list(CAMERA_NET_VEHICLE)
+ owner_factions = FACTION_LIST_HUMANOID
/obj/structure/machinery/camera/vehicle/toggle_cam_status(on = FALSE)
if(on)
@@ -92,6 +93,7 @@
/obj/structure/machinery/camera/autoname/almayer
name = "military-grade camera"
network = list(CAMERA_NET_ALMAYER)
+ owner_factions = FACTION_LIST_MARINE_WY
/obj/structure/machinery/camera/autoname/almayer/containment
name = "containment camera"
@@ -104,6 +106,7 @@
/obj/structure/machinery/camera/autoname/almayer/containment/hidden
network = list(CAMERA_NET_CONTAINMENT_HIDDEN)
+ owner_factions = FACTION_LIST_WY
/obj/structure/machinery/camera/autoname/almayer/containment/ares
name = "ares core camera"
@@ -125,6 +128,7 @@
colony_camera_mapload = FALSE
emp_proof = TRUE
+ owner_factions = FACTION_LIST_HUMANOID
/obj/structure/machinery/camera/autoname/lz_camera/ex_act()
return
diff --git a/code/game/machinery/computer/fax_responder_spy.dm b/code/game/machinery/computer/fax_responder_spy.dm
new file mode 100644
index 000000000000..e179425ae6be
--- /dev/null
+++ b/code/game/machinery/computer/fax_responder_spy.dm
@@ -0,0 +1,329 @@
+/obj/structure/machinery/computer/spy_camera
+ name = "remote monitoring computer"
+
+ icon_state = "terminal"
+
+ var/mob/hologram/spy_camera/spy_eye
+ var/spy_range = 5
+ var/spy_faction = FACTION_NEUTRAL
+
+ var/turf/last_location
+ var/turf/start_location
+
+ /// Computer and Spycam can only be used if this variable is cleared
+ var/locked = FALSE
+
+/obj/structure/machinery/computer/spy_camera/attackby(obj/I as obj, mob/user as mob) //Can't break or disassemble.
+ return
+
+/obj/structure/machinery/computer/spy_camera/bullet_act(obj/projectile/Proj) //Can't shoot it
+ return FALSE
+
+/obj/structure/machinery/computer/spy_camera/proc/set_operator(mob/living/carbon/human/new_operator)
+ if(!istype(new_operator))
+ return
+ remove_current_operator()
+
+ operator = new_operator
+ var/datum/mob_hud/spy_hud = GLOB.huds[MOB_HUD_SPYCAMS]
+ spy_hud.add_hud_to(new_operator, src)
+ RegisterSignal(operator, COMSIG_PARENT_QDELETING, PROC_REF(remove_current_operator))
+ RegisterSignal(operator, COMSIG_MOVABLE_MOVED, PROC_REF(remove_current_operator))
+
+ if(!last_location)
+ last_location = loc
+
+ start_location = last_location
+
+ spy_eye = new(last_location, new_operator, src)
+ //RegisterSignal(eye, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(check_and_set_zlevel))
+ RegisterSignal(spy_eye, COMSIG_PARENT_QDELETING, PROC_REF(remove_current_operator))
+
+/obj/structure/machinery/computer/spy_camera/proc/remove_current_operator()
+ SIGNAL_HANDLER
+ if(!operator) return
+
+ if(spy_eye)
+ last_location = spy_eye.loc
+ if(spy_eye.gc_destroyed)
+ spy_eye = null
+ else
+ QDEL_NULL(spy_eye)
+
+ UnregisterSignal(operator, list(
+ COMSIG_PARENT_QDELETING,
+ COMSIG_MOVABLE_PRE_MOVE,
+ COMSIG_MOB_POST_CLICK
+ ))
+ operator.update_sight()
+ var/datum/mob_hud/spy_hud = GLOB.huds[MOB_HUD_SPYCAMS]
+ spy_hud.remove_hud_from(operator, src)
+ operator = null
+
+/obj/structure/machinery/computer/spy_camera/attack_hand(mob/living/carbon/human/pos_operator)
+ if(..())
+ return
+
+ if(!istype(pos_operator))
+ return
+
+ if(locked || (!(pos_operator.faction == FACTION_FAX) && !(spy_faction in pos_operator.faction_group)))
+ to_chat(pos_operator, SPAN_WARNING("The remote camera system is locked out!"))
+ return FALSE
+
+ if(operator && operator.stat == CONSCIOUS)
+ to_chat(pos_operator, SPAN_WARNING("Someone is already using this computer!"))
+ return
+
+ if(tgui_alert(pos_operator, "Change the camera focus?", "Spycam Computer", list("Yes", "No")) == "Yes")
+ var/obj/effect/landmark/spycam_start/start_point = tgui_input_list(pos_operator, "Where do you want to focus the camera?", "Camera Focus", GLOB.spycam_starts)
+ if(!start_point)
+ return
+ last_location = start_point.loc
+
+ set_operator(pos_operator)
+
+
+/obj/effect/landmark/spycam_start
+ name = "Spycam Landmark"
+ icon_state = "spycam"
+
+/obj/effect/landmark/spycam_start/Initialize()
+ . = ..()
+ name = "Spycam [get_area_name(src, TRUE)]"
+ GLOB.spycam_starts += src
+
+/obj/effect/landmark/spycam_start/Destroy()
+ GLOB.spycam_starts -= src
+ return ..()
+
+/mob/hologram/spy_camera
+ name = "Spy Camera"
+ motion_sensed = FALSE
+ icon_state = "spycam"
+
+ color = "#10948d"
+
+ hud_possible = list(SPYCAM_HUD)
+ hears_speech = TRUE
+
+ var/mob/living/carbon/is_watching
+
+ var/spy_range = 5
+ var/spy_faction = FACTION_NEUTRAL
+
+ var/list/temporary_list = list()
+ var/list/temporary_list_2 = list()
+
+ ///Whether or not the camera is on cooldown for a warning message it can't move to a certain tile, locked to one message every 3 seconds.
+ var/move_warn = FALSE
+
+
+/mob/hologram/spy_camera/Initialize(mapload, mob/living/carbon/spy_operator, obj/structure/machinery/computer/spy_camera/console)
+ if(!console || !spy_operator)
+ return INITIALIZE_HINT_QDEL
+
+ if(!istype(console))
+ stack_trace("Tried to initialize a /mob/hologram/spy_camera on type ([console.type])")
+ return INITIALIZE_HINT_QDEL
+
+ spy_range = console.spy_range
+ spy_faction = console.spy_faction
+ faction = spy_faction
+
+ switch(spy_faction)
+ if(FACTION_MARINE, FACTION_MARSHAL)
+ color = "#0947bb"
+ if(FACTION_CLF)
+ color = "#717fbd"
+ if(FACTION_UPP)
+ color = "#0f3d11"
+ if(FACTION_TWE)
+ color = "#b350c0"
+ if(FACTION_WY)
+ color = "#b6b6b6"
+
+ . = ..()
+
+ name = "Spy Camera ([spy_faction])"
+ RegisterSignal(spy_operator, COMSIG_MOB_PRE_CLICK, PROC_REF(handle_overwatch))
+ //RegisterSignal(spy_operator, COMSIG_XENO_OVERWATCH_XENO, PROC_REF(start_watching))
+ //RegisterSignal(spy_operator, list(
+ // COMSIG_XENO_STOP_OVERWATCH,
+ // COMSIG_XENO_STOP_OVERWATCH_XENO
+ //), PROC_REF(stop_watching))
+ RegisterSignal(src, COMSIG_MOVABLE_TURF_ENTER, PROC_REF(can_spy_turf))
+
+ med_hud_set_status()
+ add_to_all_mob_huds()
+
+ spy_operator.sight |= SEE_TURFS|SEE_OBJS
+
+/mob/hologram/spy_camera/proc/exit_hologram()
+ SIGNAL_HANDLER
+ qdel(src)
+
+/mob/hologram/spy_camera/handle_move(mob/living/carbon/human/spy_operator, NewLoc, direct)
+ if(is_watching && (can_spy_turf(src, is_watching.loc) & COMPONENT_TURF_DENY_MOVEMENT))
+ return COMPONENT_OVERRIDE_MOVE
+
+ return ..()
+
+/mob/hologram/spy_camera/proc/start_watching(mob/living/carbon/human/source_mob, mob/living/carbon/human/target_mob)
+ SIGNAL_HANDLER
+ forceMove(target_mob)
+ is_watching = target_mob
+
+ RegisterSignal(target_mob, COMSIG_PARENT_QDELETING, PROC_REF(target_watching_qdeleted))
+ return
+
+// able to stop watching here before the loc is set to null
+/mob/hologram/spy_camera/proc/target_watching_qdeleted(mob/living/carbon/target)
+ SIGNAL_HANDLER
+ stop_watching(linked_mob, target)
+
+/mob/hologram/spy_camera/proc/stop_watching(mob/living/carbon/human/responder, mob/living/carbon/human/target)
+ SIGNAL_HANDLER
+ if(target)
+ if(loc == target)
+ var/turf/target_turf = get_turf(target)
+
+ if(target_turf)
+ forceMove(target_turf)
+ UnregisterSignal(target, COMSIG_PARENT_QDELETING)
+
+ if(!isturf(loc) || (can_spy_turf(src, loc) & COMPONENT_TURF_DENY_MOVEMENT))
+ forceMove(target.loc)
+
+ is_watching = null
+ target.reset_view()
+ return
+
+/mob/hologram/spy_camera/proc/can_spy_turf(mob/self, turf/crossing_turf)
+ SIGNAL_HANDLER
+
+ if(!crossing_turf || istype(crossing_turf, /turf/open/space) || istype(get_area(crossing_turf), /area/space))
+ return COMPONENT_TURF_DENY_MOVEMENT
+
+ if(istype(crossing_turf, /turf/closed/wall))
+ var/turf/closed/wall/crossing_wall = crossing_turf
+ if(crossing_wall.hull)
+ if(!move_warn)
+ move_warn = TRUE
+ addtimer(CALLBACK(src, PROC_REF(reset_warn)), 3 SECONDS)
+ to_chat(linked_mob, SPAN_WARNING("You cannot move the camera here, it's a solid wall!"))
+ return COMPONENT_TURF_DENY_MOVEMENT
+
+ if(is_mainship_level(z))
+ if(spy_faction in FACTION_LIST_MARINE_FAXES)
+ return COMPONENT_TURF_ALLOW_MOVEMENT
+
+ var/list/turf_area = view(spy_range, crossing_turf)
+ temporary_list = turf_area
+
+ var/list/obj/structure/machinery/camera/camera_list = list()
+ temporary_list_2 = camera_list
+
+ for(var/obj/structure/machinery/camera/nearby_camera in turf_area)
+ camera_list += nearby_camera
+ for(var/mob/living/carbon/human/local_mob in turf_area)
+ if(istype(local_mob.head, /obj/item/clothing/head/helmet/marine))
+ var/obj/item/clothing/head/helmet/marine/helm = local_mob.head
+ camera_list += helm.camera
+
+ for(var/obj/structure/machinery/camera/possible_camera in camera_list)
+ if(spy_faction in possible_camera.owner_factions)
+ return COMPONENT_TURF_ALLOW_MOVEMENT
+
+ if(!move_warn)
+ move_warn = TRUE
+ addtimer(CALLBACK(src, PROC_REF(reset_warn)), 3 SECONDS)
+ to_chat(linked_mob, SPAN_WARNING("You can't move the spy here, there's no camera you have access to nearby!"))
+ return COMPONENT_TURF_DENY_MOVEMENT
+
+/mob/hologram/spy_camera/proc/reset_warn()
+ move_warn = FALSE
+
+/mob/hologram/spy_camera/proc/is_spy_faction(atom/target_atom)
+ if(!ismob(target_atom))
+ return FALSE
+ var/mob/living/carbon/target_mob = target_atom
+ if(!(spy_faction in target_mob.faction_group))
+ return FALSE
+ return TRUE
+
+/mob/hologram/spy_camera/proc/handle_overwatch(mob/living/carbon/human/spy_operator, atom/target_atom, mods)
+ SIGNAL_HANDLER
+
+ var/turf/target_turf = get_turf(target_atom)
+ if(!istype(target_turf))
+ return
+
+ if(!mods["ctrl"])
+ return
+
+ // I want to make this mimic observer follow.
+ //if(is_spy_faction(target_atom))
+ // var/mob/living/carbon/target_mob = target_atom
+ // return COMPONENT_INTERRUPT_CLICK
+
+
+ if(!(can_spy_turf(src, target_turf) & COMPONENT_TURF_ALLOW_MOVEMENT))
+ return
+
+ forceMove(target_turf)
+
+ return COMPONENT_INTERRUPT_CLICK
+
+/mob/hologram/spy_camera/handle_view(mob/spy_operator, atom/target)
+ if(spy_operator.client)
+ spy_operator.client.perspective = EYE_PERSPECTIVE
+
+ if(is_watching)
+ spy_operator.client.eye = is_watching
+ else
+ spy_operator.client.eye = src
+
+ return COMPONENT_OVERRIDE_VIEW
+
+/mob/hologram/spy_camera/Destroy()
+ if(linked_mob)
+ linked_mob.sight &= ~(SEE_TURFS|SEE_OBJS)
+
+ remove_from_all_mob_huds()
+ is_watching = null
+
+ return ..()
+
+/mob/hologram/spy_camera/add_to_all_mob_huds()
+ var/datum/mob_hud/hud = GLOB.huds[MOB_HUD_SPYCAMS]
+ hud.add_to_hud(src)
+
+/mob/hologram/spy_camera/remove_from_all_mob_huds()
+ var/datum/mob_hud/hud = GLOB.huds[MOB_HUD_SPYCAMS]
+ hud.remove_from_hud(src)
+
+/mob/hologram/spy_camera/med_hud_set_status()
+ var/image/holder = hud_list[SPYCAM_HUD]
+ holder.icon_state = "hudeye"
+ holder.color = color
+
+
+
+/obj/structure/machinery/computer/spy_camera/uscm
+ spy_faction = FACTION_MARINE
+
+/obj/structure/machinery/computer/spy_camera/wy
+ spy_faction = FACTION_WY
+
+/obj/structure/machinery/computer/spy_camera/twe
+ spy_faction = FACTION_TWE
+
+/obj/structure/machinery/computer/spy_camera/clf
+ spy_faction = FACTION_CLF
+
+/obj/structure/machinery/computer/spy_camera/upp
+ spy_faction = FACTION_UPP
+
+/obj/structure/machinery/computer/spy_camera/cmb
+ spy_faction = FACTION_MARSHAL
diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm
index ac6de251ab45..eee97c0e2ec0 100644
--- a/code/game/machinery/computer/medical.dm
+++ b/code/game/machinery/computer/medical.dm
@@ -477,7 +477,7 @@
var/datum/asset/asset = get_asset_datum(/datum/asset/simple/paper)
var/obj/item/paper/P = new /obj/item/paper( src.loc )
P.name = text("Scan: [], []",record.fields["name"],worldtime2text())
- P.info += text("
["wylogo.png"]])
Official Weyland-Yutani Document
Scan Record
[]
\n",record.fields["name"])
+ P.info += text("["logo_wy.png"]])
Official Weyland-Yutani Document
Scan Record
[]
\n",record.fields["name"])
for(var/datum/data/record/R as anything in GLOB.data_core.medical)
if (R.fields["name"] == record.fields["name"])
if(R.fields["last_scan_time"] && R.fields["last_scan_result"])
diff --git a/code/game/machinery/fax_machine.dm b/code/game/machinery/fax_machine.dm
index 4b5df130c96d..96564a0342b7 100644
--- a/code/game/machinery/fax_machine.dm
+++ b/code/game/machinery/fax_machine.dm
@@ -68,6 +68,10 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
var/machine_id_tag
/// Whether or not the ID tag can be changed by proc.
var/fixed_id_tag = FALSE
+ /// Whether or not the next fax to be sent is a priority one.
+ var/is_priority_fax = FALSE
+ /// If this machine can send priority faxes.
+ var/can_send_priority = FALSE
/obj/structure/machinery/faxmachine/Initialize(mapload, ...)
. = ..()
@@ -274,43 +278,54 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
data["nextfaxtime"] = send_cooldown
data["faxcooldown"] = fax_cooldown
+ data["can_send_priority"] = can_send_priority
+ data["is_priority_fax"] = is_priority_fax
+
+
return data
/obj/structure/machinery/faxmachine/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
+ var/mob/user = ui.user
switch(action)
+ if("toggle_priority")
+ is_priority_fax = !is_priority_fax
+ to_chat(user, SPAN_NOTICE("Priority Alert is now [is_priority_fax ? "Enabled" : "Disabled"]."))
+ . = TRUE
+
if("send")
if(!original_fax)
- to_chat(ui.user, SPAN_NOTICE("No paper loaded."))
+ to_chat(user, SPAN_NOTICE("No paper loaded."))
return
if(istype(original_fax, /obj/item/paper_bundle))
var/obj/item/paper_bundle/bundle = original_fax
if(bundle.amount > 5)
- to_chat(ui.user, SPAN_NOTICE("\The [src] is jammed!"))
+ to_chat(user, SPAN_NOTICE("\The [src] is jammed!"))
return
copy_fax_paper()
- outgoing_fax_message(ui.user)
+ outgoing_fax_message(user, is_priority_fax)
+ is_priority_fax = FALSE
COOLDOWN_START(src, send_cooldown, fax_cooldown)
- to_chat(ui.user, "Message transmitted successfully.")
+ to_chat(user, "Message transmitted successfully.")
. = TRUE
if("ejectpaper")
if(!original_fax)
- to_chat(ui.user, SPAN_NOTICE("No paper loaded."))
- if(!ishuman(ui.user))
- to_chat(ui.user, SPAN_NOTICE("You can't do that."))
+ to_chat(user, SPAN_NOTICE("No paper loaded."))
+ if(!ishuman(user))
+ to_chat(user, SPAN_NOTICE("You can't do that."))
return
- original_fax.forceMove(ui.user.loc)
- ui.user.put_in_hands(original_fax)
- to_chat(ui.user, SPAN_NOTICE("You take \the [original_fax.name] out of \the [src]."))
+ original_fax.forceMove(user.loc)
+ user.put_in_hands(original_fax)
+ to_chat(user, SPAN_NOTICE("You take the [original_fax.name] out of [src]."))
original_fax = null
fax_paper_copy = null
photo_list = null
@@ -318,31 +333,31 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
if("insertpaper")
var/jammed = FALSE
- var/obj/item/I = ui.user.get_active_hand()
+ var/obj/item/I = user.get_active_hand()
if(istype(I, /obj/item/paper_bundle))
var/obj/item/paper_bundle/bundle = I
if(bundle.amount > 5)
jammed = TRUE
// Repeating code? This is not ideal. Why not put this functionality inside of a proc?
if(istype(I, /obj/item/paper) || istype(I, /obj/item/paper_bundle) || istype(I, /obj/item/photo))
- ui.user.drop_inv_item_to_loc(I, src)
+ user.drop_inv_item_to_loc(I, src)
original_fax = I
if(!jammed)
- to_chat(ui.user, SPAN_NOTICE("You put \the [original_fax.name] into \the [src]."))
+ to_chat(user, SPAN_NOTICE("You put the [original_fax.name] into [src]."))
else
- to_chat(ui.user, SPAN_NOTICE("\The [src] jammed! It can only accept up to five papers at once."))
+ to_chat(user, SPAN_NOTICE("[src] jammed! It can only accept up to five papers at once."))
playsound(src, "sound/machines/terminal_insert_disc.ogg", 50, TRUE)
flick("[initial(icon_state)]send", src)
. = TRUE
if("ejectid")
- if(!scan || !ishuman(ui.user))
- to_chat(ui.user, SPAN_WARNING("You can't do that."))
+ if(!scan || !ishuman(user))
+ to_chat(user, SPAN_WARNING("You can't do that."))
return
- to_chat(ui.user, SPAN_NOTICE("You take \the [scan] out of \the [src]."))
- scan.forceMove(ui.user.loc)
- if(!ui.user.get_active_hand())
- ui.user.put_in_hands(scan)
+ to_chat(user, SPAN_NOTICE("You take [scan] out of [src]."))
+ scan.forceMove(user.loc)
+ if(!user.get_active_hand())
+ user.put_in_hands(scan)
scan = null
else
scan.forceMove(src.loc)
@@ -353,11 +368,11 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
if("select")
var/last_target_department = target_department
- target_department = tgui_input_list(ui.user, "Which department?", "Choose a department", GLOB.all_fax_departments)
+ target_department = tgui_input_list(user, "Which department?", "Choose a department", GLOB.all_fax_departments)
if(!target_department)
target_department = last_target_department
if(target_department == DEPARTMENT_TARGET)
- var/new_target_machine_id = tgui_input_list(ui.user, "Which machine?", "Choose a machine code", GLOB.all_faxcodes)
+ var/new_target_machine_id = tgui_input_list(user, "Which machine?", "Choose a machine code", GLOB.all_faxcodes)
if(!new_target_machine_id)
target_department = last_target_department
else
@@ -375,7 +390,7 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
authenticated = FALSE
. = TRUE
- add_fingerprint(ui.user)
+ add_fingerprint(user)
/obj/structure/machinery/faxmachine/vv_get_dropdown()
. = ..()
@@ -422,9 +437,9 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
photo_list += list("tmp_photo[content].png" = (faxed_photo.img))
fax_paper_copy.info += "
"
-/obj/structure/machinery/faxmachine/proc/outgoing_fax_message(mob/user)
+/obj/structure/machinery/faxmachine/proc/outgoing_fax_message(mob/user, sending_priority)
- var/datum/fax/faxcontents = new(fax_paper_copy.info, photo_list, fax_paper_copy.name)
+ var/datum/fax/faxcontents = new(fax_paper_copy.info, photo_list, fax_paper_copy.name, target_department, machine_id_tag)
GLOB.fax_contents += faxcontents
@@ -472,7 +487,7 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
var/msg_ghost = SPAN_NOTICE("[the_target_department]: ")
msg_ghost += "Receiving fax via secure connection ... view message"
- send_fax(faxcontents)
+ send_fax(faxcontents, sending_priority)
announce_fax(msg_admin, msg_ghost)
@@ -500,7 +515,7 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
C << 'sound/effects/incoming-fax.ogg'
-/obj/structure/machinery/faxmachine/proc/send_fax(datum/fax/faxcontents)
+/obj/structure/machinery/faxmachine/proc/send_fax(datum/fax/faxcontents, sending_priority)
var/list/target_machines = list()
for(var/obj/structure/machinery/faxmachine/pos_target in GLOB.all_faxmachines)
if(target_department == DEPARTMENT_TARGET)
@@ -517,6 +532,7 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
flick("[initial(icon_state)]receive", target)
+ playsound(target.loc, "sound/machines/fax.ogg", 15)
// give the sprite some time to flick
spawn(30)
var/obj/item/paper/P = new(target.loc,faxcontents.photo_list)
@@ -564,7 +580,10 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
else
P.stamps += "
This paper has been sent by [machine_id_tag]."
P.overlays += stampoverlay
- playsound(target.loc, "sound/items/polaroid1.ogg", 15, 1)
+ if(sending_priority)
+ playsound(target.loc, "sound/machines/twobeep.ogg", 45)
+ target.langchat_speech("beeps with a priority message", get_mobs_in_view(GLOB.world_view_size, target), GLOB.all_languages, skip_language_check = TRUE, animation_style = LANGCHAT_FAST_POP, additional_styles = list("langchat_small", "emote"))
+ target.visible_message("[SPAN_BOLD(target)] beeps with a priority message.")
qdel(faxcontents)
/obj/structure/machinery/faxmachine/cmb
@@ -584,6 +603,7 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
department = DEPARTMENT_WY
target_department = "W-Y Liaison"
network = FAX_NET_WY_HC
+ can_send_priority = TRUE
/obj/structure/machinery/faxmachine/uscm
name = "\improper USCM Military Fax Machine"
@@ -596,11 +616,13 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
/obj/structure/machinery/faxmachine/uscm/command/capt
department = "Commanding Officer"
+ can_send_priority = TRUE
/obj/structure/machinery/faxmachine/uscm/command/highcom
department = DEPARTMENT_HC
target_department = "Commanding Officer"
network = FAX_NET_USCM_HC
+ can_send_priority = TRUE
/obj/structure/machinery/faxmachine/uscm/brig
name = "\improper USCM Provost Fax Machine"
@@ -614,6 +636,7 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
department = DEPARTMENT_PROVOST
target_department = "Brig"
network = FAX_NET_USCM_HC
+ can_send_priority = TRUE
/obj/structure/machinery/faxmachine/upp
name = "\improper UPP Military Fax Machine"
@@ -625,6 +648,7 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
department = DEPARTMENT_UPP
network = FAX_NET_UPP_HC
target_department = "UPP Local Operations"
+ can_send_priority = TRUE
/obj/structure/machinery/faxmachine/clf
name = "\improper Hacked General Purpose Fax Machine"
@@ -636,6 +660,7 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
department = DEPARTMENT_CLF
network = FAX_NET_CLF_HC
target_department = "CLF Local Operations"
+ can_send_priority = TRUE
/obj/structure/machinery/faxmachine/twe
name = "\improper TWE Military Fax Machine"
@@ -647,11 +672,27 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
department = DEPARTMENT_TWE
network = FAX_NET_TWE_HC
target_department = "TWE Local Operations"
+ can_send_priority = TRUE
/obj/structure/machinery/faxmachine/press/highcom
department = DEPARTMENT_PRESS
network = FAX_NET_PRESS_HC
target_department = "General Public"
+ can_send_priority = TRUE
+
+/obj/structure/machinery/faxmachine/Initialize(mapload, ...)
+ . = ..()
+
+ if(mapload && (department in HIGHCOM_DEPARTMENTS))
+ for(var/datum/fax/fax as anything in GLOB.fax_contents)
+ if(fax.department != department)
+ continue
+
+ var/obj/item/paper/paper = new(get_turf(src))
+ paper.info = fax.data
+ paper.update_icon()
+
+ paper.stamps += "
This paper has been sent by [fax.fax_id_tag]."
///The deployed fax machine backpack
/obj/structure/machinery/faxmachine/backpack
@@ -748,13 +789,21 @@ GLOBAL_LIST_EMPTY(all_faxcodes)
var/list/photo_list
var/paper_name
-/datum/fax/New(new_data, new_photo_list, new_name)
+ /// Where this fax was sent to
+ var/department
+
+ /// The ID tag of the fax machine that sent this
+ var/fax_id_tag
+
+/datum/fax/New(new_data, new_photo_list, new_name, department, fax_id_tag)
. = ..()
data = new_data
photo_list = new_photo_list
if(new_name != "paper")
paper_name = new_name
+ src.department = department
+ src.fax_id_tag = fax_id_tag
/obj/structure/machinery/faxmachine/proc/is_department_responder_awake(target_department)
diff --git a/code/game/machinery/vending/vending_types.dm b/code/game/machinery/vending/vending_types.dm
index ad6f201d8662..66daf5b036b5 100644
--- a/code/game/machinery/vending/vending_types.dm
+++ b/code/game/machinery/vending/vending_types.dm
@@ -442,7 +442,7 @@
/obj/item/tool/pen = 10,
/obj/item/tool/pen/blue = 10,
/obj/item/tool/pen/red = 10,
- /obj/item/tool/pen/fountain = 3,
+ /obj/item/tool/pen/multicolor/fountain = 3,
/obj/item/storage/fancy/cigarettes/trading_card = 20,
/obj/item/storage/fancy/trading_card = 20,
/obj/item/toy/trading_card = 50,
@@ -473,7 +473,7 @@
/obj/item/tool/pen = 2,
/obj/item/tool/pen/blue = 2,
/obj/item/tool/pen/red = 2,
- /obj/item/tool/pen/fountain = 30,
+ /obj/item/tool/pen/multicolor/fountain = 30,
/obj/item/storage/fancy/cigarettes/trading_card = 30,
/obj/item/storage/fancy/trading_card = 20,
/obj/item/toy/trading_card = 5,
diff --git a/code/game/objects/effects/spawners/random.dm b/code/game/objects/effects/spawners/random.dm
index 0deac38469af..dbcd8b3c8101 100644
--- a/code/game/objects/effects/spawners/random.dm
+++ b/code/game/objects/effects/spawners/random.dm
@@ -203,7 +203,7 @@
/obj/item/storage/belt/champion,\
/obj/item/tool/soap/deluxe,\
/obj/item/tool/pickaxe/silver,\
- /obj/item/tool/pen/invisible,\
+ /obj/item/tool/pen/white,\
/obj/item/explosive/grenade/smokebomb,\
/obj/item/corncob,\
/obj/item/poster,\
diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm
index 21159e2bf94b..089886db0e56 100644
--- a/code/game/objects/items/devices/radio/encryptionkey.dm
+++ b/code/game/objects/items/devices/radio/encryptionkey.dm
@@ -310,12 +310,12 @@
/obj/item/device/encryptionkey/highcom
name = "\improper USCM High Command Radio Encryption Key"
icon_state = "binary_key"
- channels = list(RADIO_CHANNEL_HIGHCOM = TRUE, SQUAD_SOF = TRUE, RADIO_CHANNEL_PROVOST = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = FALSE, SQUAD_MARINE_2 = FALSE, SQUAD_MARINE_3 = FALSE, SQUAD_MARINE_4 = FALSE, SQUAD_MARINE_5 = FALSE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = FALSE, RADIO_CHANNEL_JTAC = FALSE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_HIGHCOM = TRUE, SQUAD_SOF = TRUE, RADIO_CHANNEL_PROVOST = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = FALSE, SQUAD_MARINE_2 = FALSE, SQUAD_MARINE_3 = FALSE, SQUAD_MARINE_4 = FALSE, SQUAD_MARINE_5 = FALSE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = FALSE, RADIO_CHANNEL_JTAC = FALSE, RADIO_CHANNEL_INTEL = TRUE, RADIO_CHANNEL_ALMAYER = TRUE)
/obj/item/device/encryptionkey/provost
name = "\improper USCM Provost Radio Encryption Key"
icon_state = "sec_key"
- channels = list(RADIO_CHANNEL_PROVOST = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = FALSE, SQUAD_MARINE_2 = FALSE, SQUAD_MARINE_3 = FALSE, SQUAD_MARINE_4 = FALSE, SQUAD_MARINE_5 = FALSE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = FALSE, RADIO_CHANNEL_JTAC = FALSE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_PROVOST = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = FALSE, SQUAD_MARINE_2 = FALSE, SQUAD_MARINE_3 = FALSE, SQUAD_MARINE_4 = FALSE, SQUAD_MARINE_5 = FALSE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = FALSE, RADIO_CHANNEL_JTAC = FALSE, RADIO_CHANNEL_INTEL = TRUE, RADIO_CHANNEL_ALMAYER = TRUE)
/obj/item/device/encryptionkey/contractor
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index 76585f27f605..f9bde46f93d8 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -45,6 +45,9 @@
///The type of minimap this headset is added to
var/minimap_type = MINIMAP_FLAG_USCM
+ var/obj/item/device/radio/listening_bug/spy_bug
+ var/spy_bug_type
+
var/mob/living/carbon/human/wearer
/obj/item/device/radio/headset/Initialize()
@@ -67,8 +70,15 @@
if(GLOB.radiochannels[cycled_channel] == frequency)
default_freq = cycled_channel
+ if(spy_bug_type)
+ spy_bug = new spy_bug_type
+ spy_bug.forceMove(src)
+
/obj/item/device/radio/headset/Destroy()
wearer = null
+ if(spy_bug)
+ qdel(spy_bug)
+ spy_bug = null
QDEL_NULL_LIST(keys)
return ..()
@@ -569,6 +579,12 @@
maximum_keys = 5
initial_keys = list(/obj/item/device/encryptionkey/mcom/cl)
+ spy_bug_type = /obj/item/device/radio/listening_bug/radio_linked/fax/wy
+
+/obj/item/device/radio/headset/almayer/mcl/Initialize()
+ . = ..()
+ spy_bug.nametag = "CL Radio"
+
/obj/item/device/radio/headset/almayer/reporter
name = "reporter radio headset"
desc = "Used by the combat correspondent to get the scoop. Channels are as follows: :v - marine command, :a - alpha squad, :b - bravo squad, :c - charlie squad, :d - delta squad, :n - engineering, :m - medbay, :u - requisitions, :j - JTAC, :t - intel."
diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm
index baef29b84e81..2bfc9054b1a0 100644
--- a/code/game/objects/items/devices/radio/intercom.dm
+++ b/code/game/objects/items/devices/radio/intercom.dm
@@ -93,3 +93,16 @@
/obj/item/device/radio/intercom/devana
name = "dropship devana intercom"
frequency = UPP_DS2_FREQ
+
+/obj/item/device/radio/intercom/fax
+ name = "Monitoring Frequency Speaker"
+ canhear_range = 4
+
+/obj/item/device/radio/intercom/fax/wy
+ frequency = FAX_WY_FREQ
+
+/obj/item/device/radio/intercom/fax/uscm_hc
+ frequency = FAX_USCM_HC_FREQ
+
+/obj/item/device/radio/intercom/fax/uscm_pvst
+ frequency = FAX_USCM_PVST_FREQ
diff --git a/code/game/objects/items/devices/radio/listening_bugs.dm b/code/game/objects/items/devices/radio/listening_bugs.dm
index 5ca8503196af..d498a7247f3c 100644
--- a/code/game/objects/items/devices/radio/listening_bugs.dm
+++ b/code/game/objects/items/devices/radio/listening_bugs.dm
@@ -278,3 +278,23 @@
/obj/item/device/encryptionkey/listening_bug/freq_b
name = "Listening Bug Encryption Key (B)"
channels = list(RADIO_CHANNEL_BUG_B = TRUE)
+
+
+
+///An automatically active bug used to listen to things by a Fax Responder.
+/obj/item/device/radio/listening_bug/radio_linked/fax
+ name = "Comms Relay Device"
+ subspace_switchable = FALSE
+ broadcasting = TRUE
+
+/obj/item/device/radio/listening_bug/radio_linked/fax/wy
+ frequency = FAX_WY_FREQ
+ req_one_access = list(ACCESS_WY_SENIOR_LEAD)
+
+/obj/item/device/radio/listening_bug/radio_linked/fax/uscm_pvst
+ frequency = FAX_USCM_PVST_FREQ
+ req_one_access = list(ACCESS_MARINE_CO)
+
+/obj/item/device/radio/listening_bug/radio_linked/fax/uscm_hc
+ frequency = FAX_USCM_HC_FREQ
+ req_one_access = list(ACCESS_MARINE_CO)
diff --git a/code/game/objects/items/gift_wrappaper.dm b/code/game/objects/items/gift_wrappaper.dm
index 82d4871fc4c0..98332773c3dd 100644
--- a/code/game/objects/items/gift_wrappaper.dm
+++ b/code/game/objects/items/gift_wrappaper.dm
@@ -72,7 +72,7 @@
/obj/item/storage/belt/champion,
/obj/item/tool/soap/deluxe,
/obj/item/tool/pickaxe/silver,
- /obj/item/tool/pen/invisible,
+ /obj/item/tool/pen/white,
/obj/item/explosive/grenade/smokebomb,
/obj/item/corncob,
/obj/item/poster,
diff --git a/code/game/objects/items/tools/misc_tools.dm b/code/game/objects/items/tools/misc_tools.dm
index fae2c3916448..ffae36ae76e3 100644
--- a/code/game/objects/items/tools/misc_tools.dm
+++ b/code/game/objects/items/tools/misc_tools.dm
@@ -192,6 +192,16 @@
if(on)
overlays += "+[pen_color]_tip"
+/obj/item/tool/pen/attack(mob/M as mob, mob/user as mob)
+ if(!ismob(M))
+ return
+ to_chat(user, SPAN_WARNING("You stab [M] with the pen."))
+ M.last_damage_data = create_cause_data(initial(name), user)
+ M.attack_log += text("\[[time_stamp()]\] Has been stabbed with [name] by [key_name(user)]")
+ user.attack_log += text("\[[time_stamp()]\] Used the [name] to stab [key_name(M)]")
+ msg_admin_attack("[key_name(user)] Used the [name] to stab [key_name(M)] in [get_area(user)] ([user.loc.x],[user.loc.y],[user.loc.z]).", user.loc.x, user.loc.y, user.loc.z)
+ return
+
/obj/item/tool/pen/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
. = ..()
if(!isobj(target))
@@ -283,11 +293,33 @@
name = "WY green pen"
clicky = TRUE
-/obj/item/tool/pen/invisible
- desc = "It's an invisible pen marker."
+/obj/item/tool/pen/white
+ desc = "It's a rare white ink pen."
pen_color = "white"
-/obj/item/tool/pen/fountain
+/obj/item/tool/pen/white/clicky
+ desc = "It's a WY brand extra clicky white ink pen."
+ name = "WY white pen"
+ clicky = TRUE
+
+/obj/item/tool/pen/multicolor
+ name = "multicolor pen"
+ desc = "A color switching pen!"
+ var/list/colour_list = list("red", "blue", "black")
+ var/current_colour_index = 1
+
+/obj/item/tool/pen/multicolor/attack_self(mob/living/carbon/human/user)
+ if(on)
+ current_colour_index = (current_colour_index % length(colour_list)) + 1
+ pen_color = colour_list[current_colour_index]
+ balloon_alert(user,"you twist the pen and change the ink color to [pen_color].")
+ if(clicky)
+ playsound(user.loc, 'sound/items/pen_click_on.ogg', 100, 1, 5)
+ update_pen_state()
+ else
+ ..()
+
+/obj/item/tool/pen/multicolor/fountain
desc = "A lavish testament to the ingenuity of ARMAT's craftsmanship, this fountain pen is a paragon of design and functionality. Detailed with golden accents and intricate mechanics, the pen allows for a swift change between a myriad of ink colors with a simple twist. A product of precision engineering, each mechanism inside the pen is designed to provide a seamless, effortless transition from one color to the next, creating an instrument of luxurious versatility."
desc_lore = "More than just a tool for writing, ARMAT's fountain pen is a symbol of distinction and authority within the ranks of the United States Colonial Marine Corps (USCM). It is a legacy item, exclusively handed out to the top-tier command personnel, each pen a tribute to the recipient's leadership and dedication.\n \nARMAT, renowned for their weapons technology, took a different approach in crafting this piece. The fountain pen, though seemingly a departure from their usual field, is deeply ingrained with the company's engineering philosophy, embodying precision, functionality, and robustness.\n \nThe golden accents are not mere embellishments; they're an identifier, setting apart these pens and their owners from the rest. The gold is meticulously alloyed with a durable metallic substance, granting it resilience to daily wear and tear. Such resilience is symbolic of the tenacity and perseverance required of USCM command personnel.\n \nEach pen is equipped with an intricate color changing mechanism, allowing the user to switch between various ink colors. This feature, inspired by the advanced targeting systems of ARMAT's weaponry, uses miniaturized actuators and precision-ground components to smoothly transition the ink flow. A simple twist of the pen's body activates the change, rotating the internal ink cartridges into place with mechanical grace, ready for the user's command.\n \nThe ink colors are not chosen arbitrarily. Each represents a different echelon within the USCM, allowing the pen's owner to write in the hue that corresponds with their rank or the rank of the recipient of their written orders. This acts as a silent testament to the authority of their words, as if each stroke of the pen echoes through the halls of USCM authority.\n \nDespite its ornate appearance, the pen is as robust as any ARMAT weapon, reflecting the company's commitment to reliability and durability. The metal components are corrosion-resistant, ensuring the pen's longevity, even under the challenging conditions often faced by USCM high command.\n \nThe fusion of luxury and utility, the blend of gold and metal, is an embodiment of the hard-won elegance of command, of the fusion between power and grace. It's more than a writing instrument - it's an emblem of leadership, an accolade to the dedication and strength of those who bear it. ARMAT's fountain pen stands as a monument to the precision, integrity, and courage embodied by the USCM's highest-ranking officers."
name = "fountain pen"
@@ -295,66 +327,45 @@
item_state = "fountain_pen"
item_state_slots = list(WEAR_AS_GARB = "fountain_pen")
matter = list("metal" = 20, "gold" = 10)
- var/static/list/color_list = list("red", "blue", "green", "yellow", "purple", "pink", "brown", "black", "orange") // Can add more colors as required
- var/current_color_index = 1
+ colour_list = list("red", "blue", "green", "yellow", "purple", "pink", "brown", "black", "orange") // Can add more colors as required
var/owner_name
-/obj/item/tool/pen/fountain/pickup(mob/user, silent)
+/obj/item/tool/pen/multicolor/fountain/pickup(mob/user, silent)
. = ..()
if(!owner_name)
RegisterSignal(user, COMSIG_POST_SPAWN_UPDATE, PROC_REF(set_owner), override = TRUE)
///Sets the owner of the pen to who it spawns with, requires var/source for signals
-/obj/item/tool/pen/fountain/proc/set_owner(datum/source)
+/obj/item/tool/pen/multicolor/fountain/proc/set_owner(datum/source)
SIGNAL_HANDLER
UnregisterSignal(source, COMSIG_POST_SPAWN_UPDATE)
var/mob/living/carbon/human/user = source
owner_name = user.name
-/obj/item/tool/pen/fountain/get_examine_text(mob/user)
+/obj/item/tool/pen/multicolor/fountain/get_examine_text(mob/user)
. = ..()
if(owner_name)
. += "There's a laser engraving of [owner_name] on it."
-/obj/item/tool/pen/fountain/attack_self(mob/living/carbon/human/user)
- if(on)
- current_color_index = (current_color_index % length(color_list)) + 1
- pen_color = color_list[current_color_index]
- balloon_alert(user,"you twist the pen and change the ink color to [pen_color].")
- if(clicky)
- playsound(user.loc, 'sound/items/pen_click_on.ogg', 100, 1, 5)
- update_pen_state()
- else
- ..()
-
-/obj/item/tool/pen/attack(mob/M as mob, mob/user as mob)
- if(!ismob(M))
- return
- to_chat(user, SPAN_WARNING("You stab [M] with the pen."))
-// to_chat(M, SPAN_WARNING("You feel a tiny prick!")) //That's a whole lot of meta!
- M.last_damage_data = create_cause_data(initial(name), user)
- M.attack_log += text("\[[time_stamp()]\] Has been stabbed with [name] by [key_name(user)]")
- user.attack_log += text("\[[time_stamp()]\] Used the [name] to stab [key_name(M)]")
- msg_admin_attack("[key_name(user)] Used the [name] to stab [key_name(M)] in [get_area(user)] ([user.loc.x],[user.loc.y],[user.loc.z]).", user.loc.x, user.loc.y, user.loc.z)
- return
-
+/obj/item/tool/pen/multicolor/provost
+ name = "provost pen"
+ desc = "A sleek black shell pen with the Provost Office sigil engraved into the side. It can change colors as needed for various functions within the Provost and Military Police."
+ icon_state = "provost_pen"
+ colour_list = list("blue", "green", "black", "orange", "red", "white")
/*
- * Sleepy Pens
+ * Antag pens
*/
/obj/item/tool/pen/sleepypen
desc = "It's a black ink pen with a sharp point and a carefully engraved \"Waffle Co.\""
flags_atom = FPRINT|OPENCONTAINER
flags_equip_slot = SLOT_WAIST
-
-
/obj/item/tool/pen/sleepypen/Initialize()
. = ..()
create_reagents(30)
reagents.add_reagent("chloralhydrate", 22)
-
/obj/item/tool/pen/sleepypen/attack(mob/M as mob, mob/user as mob)
if(!(istype(M,/mob)))
return
@@ -363,16 +374,10 @@
if(M.reagents) reagents.trans_to(M, 50)
return
-
-/*
- * Parapens
- */
/obj/item/tool/pen/paralysis
flags_atom = FPRINT|OPENCONTAINER
flags_equip_slot = SLOT_WAIST
-
-
/obj/item/tool/pen/paralysis/attack(mob/living/M as mob, mob/user as mob)
if(!(istype(M)))
return
@@ -387,6 +392,9 @@
reagents.add_reagent("zombiepowder", 10)
reagents.add_reagent("cryptobiolin", 15)
+/*
+ * Stamps
+ */
/obj/item/tool/stamp
name = "rubber stamp"
desc = "A rubber stamp for stamping important documents."
diff --git a/code/game/supplyshuttle.dm b/code/game/supplyshuttle.dm
index 8974eb36187f..fc5744dcbbf1 100644
--- a/code/game/supplyshuttle.dm
+++ b/code/game/supplyshuttle.dm
@@ -1141,7 +1141,7 @@ GLOBAL_DATUM_INIT(supply_controller, /datum/controller/supply, new())
temp += "Back to all categories
"
temp += SPAN_DANGER("ERR0R UNK7OWN C4T2G#!$0-
")
if(black_market_lockout)
- temp += "
Unauthorized Access Removed.
This console is currently under CMB investigation.
Thank you for your cooperation. "
+ temp += "
Unauthorized Access Removed.
This console is currently under CMB investigation.
Thank you for your cooperation. "
return
temp += "KHZKNHZH#0-"
if(!GLOB.supply_controller.mendoza_status) // he's daed
diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm
index be210ea70099..1fcd74ecc724 100644
--- a/code/game/turfs/open.dm
+++ b/code/game/turfs/open.dm
@@ -862,7 +862,8 @@
no_overlay = TRUE
supports_surgery = FALSE
-
+/turf/open/gm/river/no_overlay/sewage
+ name = "sewage"
//ELEVATOR SHAFT-----------------------------------//
diff --git a/code/game/turfs/shiva.dm b/code/game/turfs/shiva.dm
index 26e9cbb29c18..b155c46d60d2 100644
--- a/code/game/turfs/shiva.dm
+++ b/code/game/turfs/shiva.dm
@@ -186,6 +186,9 @@
/turf/open/floor/shiva/bluecorners
icon_state = "bluecorners"
+/turf/open/floor/shiva/bluecorners/east
+ dir = EAST
+
/turf/open/floor/shiva/bluecorners/west
dir = WEST
diff --git a/code/modules/admin/fax_templates.dm b/code/modules/admin/fax_templates.dm
index 2522acf92b3b..210915a3f7b8 100644
--- a/code/modules/admin/fax_templates.dm
+++ b/code/modules/admin/fax_templates.dm
@@ -7,7 +7,7 @@
dat += "body {"
dat += "margin:0 auto;"
dat += "padding:0;"
- dat += "background-image: url('[asset.get_url_mappings()["faxbackground.jpg"]]');"
+ dat += "background-image: url('[asset.get_url_mappings()["background_white.jpg"]]');"
dat += "font-family: monospace;"
dat += "}"
@@ -65,7 +65,7 @@
if(show_wy_logo)
dat += ""
- dat += "
["faxwylogo.png"]])
"
+ dat += "
["logo_wy.png"]])
"
dat += "
"
dat += ""
+ if(owner.check_whitelist_status(WHITELIST_FAX_RESPONDER))
+ dat += ""
dat += "