diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm
index d0f18a140ca..1aae642647b 100644
--- a/code/datums/uplink_item.dm
+++ b/code/datums/uplink_item.dm
@@ -441,6 +441,12 @@ var/list/uplink_items = list()
item = /obj/item/device/chameleon
cost = 7
+/datum/uplink_item/stealthy_tools/camera_bug
+ name = "Camera Bug"
+ desc = "Enables you to bug cameras to view them remotely. Adding particular items to it alters its functions."
+ item = /obj/item/device/camera_bug
+ cost = 2
+
/datum/uplink_item/stealthy_tools/dnascrambler
name = "DNA Scrambler"
desc = "A syringe with one injection that randomizes appearance and name upon use. A cheaper but less versatile alternative to an agent card and voice changer."
@@ -506,20 +512,6 @@ var/list/uplink_items = list()
item = /obj/item/clothing/glasses/thermal/syndi
cost = 6
-/*
-/datum/uplink_item/device_tools/surveillance
- name = "Camera Surveillance Kit"
- desc = "This kit contains 5 Camera bugs and one mobile receiver. Attach camera bugs to a camera to enable remote viewing."
- item = /obj/item/weapon/storage/box/syndie_kit/surveillance
- cost = 5
-
-/datum/uplink_item/device_tools/camerabugs
- name = "Camera Bugs"
- desc = "This is a Camera bug resupply giving you 5 more camera bugs."
- item = /obj/item/weapon/storage/box/surveillance
- cost = 4
-*/ //commented out until porting over TG's camera bug
-
/datum/uplink_item/device_tools/binary
name = "Binary Translator Key"
desc = "A key, that when inserted into a radio headset, allows you to listen to and talk with artificial intelligences and cybernetic organisms in binary."
diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm
index 04baae91356..310715fbeb4 100644
--- a/code/defines/obj/weapon.dm
+++ b/code/defines/obj/weapon.dm
@@ -479,42 +479,6 @@
icon_state = "power_mod"
desc = "Charging circuits for power cells."
-
-/obj/item/device/camera_bug
- name = "camera bug"
- desc = "Tiny electronic device meant to bug cameras for viewing later."
- icon = 'icons/obj/device.dmi'
- icon_state = "implant_evil"
- w_class = 1.0
- item_state = ""
- throw_speed = 4
- throw_range = 20
-
-/obj/item/weapon/camera_bug/attack_self(mob/usr as mob)
- var/list/cameras = new/list()
- for (var/obj/machinery/camera/C in cameranet.cameras)
- if (C.bugged && C.status)
- cameras.Add(C)
- if (length(cameras) == 0)
- usr << "\red No bugged functioning cameras found."
- return
-
- var/list/friendly_cameras = new/list()
-
- for (var/obj/machinery/camera/C in cameras)
- friendly_cameras.Add(C.c_tag)
-
- var/target = input("Select the camera to observe", null) as null|anything in friendly_cameras
- if (!target)
- return
- for (var/obj/machinery/camera/C in cameras)
- if (C.c_tag == target)
- target = C
- break
- if (usr.stat == 2) return
-
- usr.client.eye = target
-
/obj/item/weapon/hatchet
name = "hatchet"
desc = "A very sharp axe blade upon a short fibremetal handle. It has a long history of chopping things, but now it is used for chopping wood."
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index f0633e1a3e4..e60921e700b 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -8,21 +8,17 @@
active_power_usage = 10
layer = 5
+ var/datum/wires/camera/wires = null // Wires datum
var/list/network = list("SS13")
var/c_tag = null
var/c_tag_order = 999
- var/status = 1
+ var/status = 1.0
anchored = 1.0
- panel_open = 0 // 0 = Closed / 1 = Open
- var/indestructible = 0
- var/bugged = 0
+ var/start_active = 0 //If it ignores the random chance to start broken on round start
+ var/invuln = null
+ var/obj/item/device/camera_bug/bug = null
var/obj/item/weapon/camera_assembly/assembly = null
- var/toughness = 5 //sorta fragile
-
- // WIRES
- var/datum/wires/camera/wires = null // Wires datum
-
//OTHER
var/view_range = 7
@@ -31,15 +27,15 @@
var/light_disabled = 0
var/alarm_on = 0
var/busy = 0
-
- var/obj/item/device/camera_bug/hasbug = null
+ var/emped = 0 //Number of consecutive EMP's on this camera
/obj/machinery/camera/New()
wires = new(src)
+
assembly = new(src)
assembly.state = 4
-
- //invalidateCameraCache()
+ assembly.anchored = 1
+ assembly.update_icon()
/* // Use this to look for cameras that have the same c_tag.
for(var/obj/machinery/camera/C in cameranet.cameras)
@@ -47,61 +43,78 @@
if(C != src && C.c_tag == src.c_tag && tempnetwork.len)
world.log << "[src.c_tag] [src.x] [src.y] [src.z] conflicts with [C.c_tag] [C.x] [C.y] [C.z]"
*/
- if(!src.network || src.network.len < 1)
- if(loc)
- error("[src.name] in [get_area(src)] (x:[src.x] y:[src.y] z:[src.z] has errored. [src.network?"Empty network list":"Null network list"]")
- else
- error("[src.name] in [get_area(src)]has errored. [src.network?"Empty network list":"Null network list"]")
- ASSERT(src.network)
- ASSERT(src.network.len > 0)
..()
-/obj/machinery/camera/Del()
- if(!alarm_on)
- triggerCameraAlarm()
-
- cancelCameraAlarm()
+/obj/machinery/camera/initialize()
+ if(z == 1 && prob(3) && !start_active)
+ deactivate()
+
+/obj/machinery/camera/Destroy()
+ deactivate(null, 0) //kick anyone viewing out
+ if(assembly)
+ qdel(assembly)
+ assembly = null
+ if(istype(bug))
+ bug.bugged_cameras -= src.c_tag
+ if(bug.current == src)
+ bug.current = null
+ bug = null
+ del(wires)
+ cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that
..()
/obj/machinery/camera/emp_act(severity)
if(!isEmpProof())
if(prob(100/severity))
- //invalidateCameraCache()
+ icon_state = "[initial(icon_state)]emp"
+ var/list/previous_network = network
+ network = list()
+ cameranet.removeCamera(src)
stat |= EMPED
SetLuminosity(0)
- kick_viewers()
triggerCameraAlarm()
- update_icon()
-
+ emped = emped+1 //Increase the number of consecutive EMP's
+ var/thisemp = emped //Take note of which EMP this proc is for
spawn(900)
- stat &= ~EMPED
- cancelCameraAlarm()
- update_icon()
- //invalidateCameraCache()
+ if(loc) //qdel limbo
+ if(emped == thisemp) //Only fix it if the camera hasn't been EMP'd again
+ network = previous_network
+ icon_state = initial(icon_state)
+ stat &= ~EMPED
+ cancelCameraAlarm()
+ if(can_use())
+ cameranet.addCamera(src)
+ emped = 0 //Resets the consecutive EMP count
+ for(var/mob/O in mob_list)
+ if (O.client && O.client.eye == src)
+ O.unset_machine()
+ O.reset_view(null)
+ O << "The screen bursts into static."
..()
-/obj/machinery/camera/bullet_act(var/obj/item/projectile/P)
- if(P.damage_type == BRUTE || P.damage_type == BURN)
- take_damage(P.damage)
-/obj/machinery/camera/ex_act(severity)
- if(indestructible)
+/obj/machinery/camera/ex_act(severity, target)
+ if(src.invuln)
return
-
- //camera dies if an explosion touches it!
- if(severity <= 2 || prob(50))
- destroy()
-
- ..() //and give it the regular chance of being deleted outright
-
+ else
+ ..()
+ return
/obj/machinery/camera/blob_act()
+ qdel(src)
return
-
-/obj/machinery/camera/attack_paw(mob/living/carbon/alien/humanoid/user as mob)
+
+/obj/machinery/camera/proc/setViewRange(var/num = 7)
+ src.view_range = num
+ cameranet.updateVisibility(src, 0)
+
+/obj/machinery/camera/proc/shock(var/mob/living/user)
if(!istype(user))
return
- if(indestructible)
+ user.electrocute_act(10, src)
+
+/obj/machinery/camera/attack_paw(mob/living/carbon/alien/humanoid/user as mob)
+ if(!istype(user))
return
user.do_attack_animation(src)
status = 0
@@ -111,22 +124,12 @@
add_hiddenprint(user)
deactivate(user,0)
-/obj/machinery/camera/hitby(AM as mob|obj)
- ..()
- if (istype(AM, /obj))
- var/obj/O = AM
- if (O.throwforce >= src.toughness)
- visible_message("[src] was hit by [O].")
- take_damage(O.throwforce)
+/obj/machinery/camera/attackby(W as obj, mob/living/user as mob, params)
+ var/msg = "You attach [W] into the assembly inner circuits."
+ var/msg2 = "The camera already has that upgrade!"
-/obj/machinery/camera/proc/setViewRange(var/num = 7)
- src.view_range = num
- cameranet.updateVisibility(src, 0)
-
-/obj/machinery/camera/attackby(obj/W as obj, mob/living/user as mob, params)
- //invalidateCameraCache()
// DECONSTRUCTION
- if(isscrewdriver(W))
+ if(istype(W, /obj/item/weapon/screwdriver))
//user << "You start to [panel_open ? "close" : "open"] the camera's panel."
//if(toggle_panel(user)) // No delay because no one likes screwdrivers trying to be hip and have a duration cooldown
panel_open = !panel_open
@@ -134,21 +137,46 @@
"You screw the camera's panel [panel_open ? "open" : "closed"].")
playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
- else if((iswirecutter(W) || ismultitool(W)) && panel_open)
- interact(user)
+ else if((istype(W, /obj/item/weapon/wirecutters) || istype(W, /obj/item/device/multitool)) && panel_open)
+ wires.Interact(user)
- else if(iswelder(W) && (wires.CanDeconstruct() || (stat & BROKEN)))
+ else if(istype(W, /obj/item/weapon/weldingtool) && wires.CanDeconstruct())
if(weld(W, user))
- if (stat & BROKEN)
- new /obj/item/stack/cable_coil(src.loc, length=2)
- else if(assembly)
- assembly.loc = src.loc
- assembly.state = 1
- new /obj/item/stack/cable_coil(src.loc, length=2)
- del(src)
+ user << "You unweld the camera leaving it as just a frame screwed to the wall."
+ if(!assembly)
+ assembly = new()
+ assembly.loc = src.loc
+ assembly.state = 1
+ assembly.dir = src.dir
+ assembly.update_icon()
+ assembly = null
+ qdel(src)
+ return
+ else if(istype(W, /obj/item/device/analyzer) && panel_open) //XRay
+ if(!isXRay())
+ upgradeXRay()
+ qdel(W)
+ user << "[msg]"
+ else
+ user << "[msg2]"
+
+ else if(istype(W, /obj/item/stack/sheet/mineral/plasma) && panel_open)
+ if(!isEmpProof())
+ upgradeEmpProof()
+ user << "[msg]"
+ qdel(W)
+ else
+ user << "[msg2]"
+ else if(istype(W, /obj/item/device/assembly/prox_sensor) && panel_open)
+ if(!isMotion())
+ upgradeMotion()
+ user << "[msg]"
+ qdel(W)
+ else
+ user << "[msg2]"
// OTHER
- else if (can_use() && (istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/device/pda)) && isliving(user))
+ else if ((istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/device/pda)) && isliving(user))
var/mob/living/U = user
var/obj/item/weapon/paper/X = null
var/obj/item/device/pda/P = null
@@ -163,140 +191,90 @@
P = W
itemname = P.name
info = P.notehtml
- U << "You hold \a [itemname] up to the camera ..."
- for(var/mob/living/silicon/ai/O in living_mob_list)
- if(!O.client) continue
- if(U.name == "Unknown") O << "[U] holds \a [itemname] up to one of your cameras ..."
- else O << "[U] holds \a [itemname] up to one of your cameras ..."
- O << browse(text("
[][]", itemname, info), text("window=[]", itemname))
+ U << "You hold \the [itemname] up to the camera ..."
+ U.changeNext_move(CLICK_CD_MELEE)
for(var/mob/O in player_list)
- if (istype(O.machine, /obj/machinery/computer/security))
- var/obj/machinery/computer/security/S = O.machine
- if (S.current == src)
- O << "[U] holds \a [itemname] up to one of the cameras ..."
- O << browse(text("[][]", itemname, info), text("window=[]", itemname))
-
- else if (istype(W, /obj/item/device/camera_bug) && panel_open)
+ if(istype(O, /mob/living/silicon/ai))
+ var/mob/living/silicon/ai/AI = O
+ if(U.name == "Unknown") AI << "[U] holds \a [itemname] up to one of your cameras ..."
+ else AI << "[U] holds \a [itemname] up to one of your cameras ..."
+ AI.last_paper_seen = "[itemname][info]"
+ else if (O.client && O.client.eye == src)
+ O << "[U] holds \a [itemname] up to one of the cameras ..."
+ O << browse(text("[][]", itemname, info), text("window=[]", itemname))
+ else if (istype(W, /obj/item/device/camera_bug))
if (!src.can_use())
- user << "\blue Camera non-functional"
+ user << "Camera non-functional."
return
+ if(istype(src.bug))
+ user << "Camera bug removed."
+ src.bug.bugged_cameras -= src.c_tag
+ src.bug = null
else
- user << "\blue Camera bugged."
- user.drop_item(W)
- hasbug = W
- src.bugged = 1
- else if (iscrowbar(W) && panel_open && src.hasbug)
- user << "\blue You retrieve \the [hasbug]"
- user.put_in_hands(hasbug)
- hasbug = null
- deactivatebug(user)
- else if(W.damtype == BRUTE || W.damtype == BURN) //bashing cameras
- if (W.force >= src.toughness)
- visible_message("[src] has been [pick(W.attack_verb)] with [W] by [user]!")
- if (istype(W, /obj/item)) //is it even possible to get into attackby() with non-items?
- var/obj/item/I = W
- if (I.hitsound)
- playsound(loc, I.hitsound, 50, 1, -1)
- take_damage(W.force)
-
+ user << "Camera bugged."
+ src.bug = W
+ src.bug.bugged_cameras[src.c_tag] = src
+ else if(istype(W, /obj/item/weapon/melee/energy/blade))//Putting it here last since it's a special case. I wonder if there is a better way to do these than type casting.
+ deactivate(user,2)//Here so that you can disconnect anyone viewing the camera, regardless if it's on or off.
+ var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
+ spark_system.set_up(5, 0, loc)
+ spark_system.start()
+ playsound(loc, 'sound/weapons/blade1.ogg', 50, 1)
+ playsound(loc, "sparks", 50, 1)
+ visible_message("[user] has sliced the camera apart with an energy blade!")
+ qdel(src)
+ else if(istype(W, /obj/item/device/laser_pointer))
+ var/obj/item/device/laser_pointer/L = W
+ L.laser_act(src, user)
else
..()
-
-/obj/machinery/camera/proc/deactivatebug(user as mob)
- for(var/mob/O in player_list)
- if(istype(O.machine, /obj/item/device/handtv))
- var/obj/item/device/handtv/S = O.machine
- if (S.current == src)
- O.unset_machine()
- O.reset_view(null)
- O << "The screen bursts into static."
+ return
/obj/machinery/camera/proc/deactivate(user as mob, var/choice = 1)
- if(choice != 1)
- //legacy support, if choice is != 1 then just kick viewers without changing status
- kick_viewers()
- else
- //invalidateCameraCache()
- set_status( !src.status )
+ if(choice==1)
+ status = !( src.status )
if (!(src.status))
- visible_message("\red [user] has deactivated [src]!")
+ if(user)
+ visible_message("[user] deactivates [src]!")
+ add_hiddenprint(user)
+ else
+ visible_message("\The [src] deactivates!")
playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)
icon_state = "[initial(icon_state)]1"
- add_hiddenprint(user)
+
else
- visible_message("\red [user] has reactivated [src]!")
+ if(user)
+ visible_message("[user] reactivates [src]!")
+ add_hiddenprint(user)
+ else
+ visible_message("\The [src] reactivates!")
playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)
icon_state = initial(icon_state)
- add_hiddenprint(user)
-/obj/machinery/camera/proc/take_damage(var/force, var/message)
- //prob(25) gives an average of 3-4 hits
- if (force >= toughness && (force > toughness*4 || prob(25)))
- destroy()
-
-//Used when someone breaks a camera
-/obj/machinery/camera/proc/destroy()
- //invalidateCameraCache()
- stat |= BROKEN
- kick_viewers()
- triggerCameraAlarm()
- update_icon()
-
- //sparks
- var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
- spark_system.set_up(5, 0, loc)
- spark_system.start()
- playsound(loc, "sparks", 50, 1)
-
-/obj/machinery/camera/proc/set_status(var/newstatus)
- if (status != newstatus)
- status = newstatus
- //invalidateCameraCache()
- // now disconnect anyone using the camera
- //Apparently, this will disconnect anyone even if the camera was re-activated.
- //I guess that doesn't matter since they couldn't use it anyway?
- kick_viewers()
-
-//This might be redundant, because of check_eye()
-/obj/machinery/camera/proc/kick_viewers()
+ // now disconnect anyone using the camera
+ //Apparently, this will disconnect anyone even if the camera was re-activated.
+ //I guess that doesn't matter since they can't use it anyway?
for(var/mob/O in player_list)
- if (istype(O.machine, /obj/machinery/computer/security))
- var/obj/machinery/computer/security/S = O.machine
- if (S.current == src)
- O.unset_machine()
- O.reset_view(null)
- O << "The screen bursts into static."
-
-/obj/machinery/camera/update_icon()
- if (!status || (stat & BROKEN))
- icon_state = "[initial(icon_state)]1"
- else if (stat & EMPED)
- icon_state = "[initial(icon_state)]emp"
- else
- icon_state = initial(icon_state)
+ if (O.client && O.client.eye == src)
+ O.unset_machine()
+ O.reset_view(null)
+ O << "The screen bursts into static."
/obj/machinery/camera/proc/triggerCameraAlarm()
alarm_on = 1
- if(!get_area(src))
- return
-
for(var/mob/living/silicon/S in mob_list)
S.triggerAlarm("Camera", get_area(src), list(src), src)
/obj/machinery/camera/proc/cancelCameraAlarm()
alarm_on = 0
- if(!get_area(src))
- return
-
for(var/mob/living/silicon/S in mob_list)
S.cancelAlarm("Camera", get_area(src), src)
-//if false, then the camera is listed as DEACTIVATED and cannot be used
/obj/machinery/camera/proc/can_use()
if(!status)
return 0
- if(stat & (EMPED|BROKEN))
+ if(stat & EMPED)
return 0
return 1
@@ -318,13 +296,13 @@
//If someone knows a better way to do this, let me know. -Giacom
switch(i)
if(NORTH)
- dir = SOUTH
+ src.dir = SOUTH
if(SOUTH)
- dir = NORTH
+ src.dir = NORTH
if(WEST)
- dir = EAST
+ src.dir = EAST
if(EAST)
- dir = WEST
+ src.dir = WEST
break
//Return a working camera that can see a given mob
@@ -353,7 +331,7 @@
return 0
// Do after stuff here
- user << "You start to weld the [src].."
+ user << "You start to weld [src]."
playsound(src.loc, 'sound/items/Welder.ogg', 50, 1)
WT.eyecheck(user)
busy = 1
@@ -364,14 +342,3 @@
return 1
busy = 0
return 0
-
-/obj/machinery/camera/interact(mob/living/user as mob)
- if(!panel_open || istype(user, /mob/living/silicon/ai))
- return
-
- if(stat & BROKEN)
- user << "\The [src] is broken."
- return
-
- user.set_machine(src)
- wires.Interact(user)
diff --git a/code/game/objects/items/devices/camera_bug.dm b/code/game/objects/items/devices/camera_bug.dm
new file mode 100644
index 00000000000..057cdb765fc
--- /dev/null
+++ b/code/game/objects/items/devices/camera_bug.dm
@@ -0,0 +1,371 @@
+#define VANILLA_BUG 0
+#define UNIVERSAL_BUG 1
+#define NETWORK_BUG 2
+#define SABOTAGE_BUG 3
+#define ADVANCED_BUG 4
+#define ADMIN_BUG 5
+
+#define BUGMODE_LIST 0
+#define BUGMODE_MONITOR 1
+#define BUGMODE_TRACK 2
+
+
+
+/obj/item/device/camera_bug
+ name = "camera bug"
+ desc = "For illicit snooping through the camera network."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "camera_bug"
+ w_class = 1.0
+ item_state = "camera_bug"
+ icon_override = 'icons/mob/in-hand/tools.dmi'
+ throw_speed = 4
+ throw_range = 20
+
+ var/obj/machinery/camera/current = null
+ var/obj/item/expansion = null
+ var/bugtype = VANILLA_BUG
+
+ var/last_net_update = 0
+ var/last_bugtype = VANILLA_BUG
+ var/list/bugged_cameras = list()
+ var/skip_bugcheck = 0
+
+ var/track_mode = BUGMODE_LIST
+ var/last_tracked = 0
+ var/refresh_interval = 50
+
+ var/tracked_name = null
+ var/atom/tracking = null
+
+ var/last_found = null
+ var/last_seen = null
+
+/obj/item/device/camera_bug/New()
+ ..()
+ processing_objects += src
+
+/obj/item/device/camera_bug/Destroy()
+ if(expansion)
+ qdel(expansion)
+ expansion = null
+ del(src)
+/* Easier to just call del() than this nonsense
+ get_cameras()
+ for(var/cam_tag in bugged_cameras)
+ var/obj/machinery/camera/camera = bugged_cameras[cam_tag]
+ if(camera.bug == src)
+ camera.bug = null
+ bugged_cameras = list()
+ if(tracking)
+ tracking = null
+ ..()
+*/
+
+/obj/item/device/camera_bug/interact(var/mob/user = usr)
+ var/datum/browser/popup = new(user, "camerabug","Camera Bug",nref=src)
+ popup.set_content(menu(get_cameras()))
+ popup.open()
+
+/obj/item/device/camera_bug/attack_self(mob/user as mob)
+ user.set_machine(src)
+ interact(user)
+
+/obj/item/device/camera_bug/check_eye(var/mob/user as mob)
+ if (user.stat || loc != user || !user.canmove || user.eye_blind || !current)
+ user.reset_view(null)
+ user.unset_machine()
+ return null
+
+ var/turf/T = get_turf(user.loc)
+ if(T.z != current.z || (!skip_bugcheck && current.bug != src) || !current.can_use())
+ user << "[src] has lost the signal."
+ current = null
+ user.reset_view(null)
+ user.unset_machine()
+ return null
+
+ return 1
+
+/obj/item/device/camera_bug/proc/get_cameras()
+ if(bugtype != last_bugtype || ( (bugtype in list(UNIVERSAL_BUG,NETWORK_BUG,ADMIN_BUG)) && world.time > (last_net_update + 100)))
+ bugged_cameras = list()
+ last_bugtype = bugtype
+ for(var/obj/machinery/camera/camera in cameranet.cameras)
+ if(camera.stat || !camera.can_use())
+ continue
+ switch(bugtype)
+ if(VANILLA_BUG,SABOTAGE_BUG,ADVANCED_BUG)
+ if(camera.bug == src)
+ bugged_cameras[camera.c_tag] = camera
+ if(UNIVERSAL_BUG)
+ if(camera.bug)
+ bugged_cameras[camera.c_tag] = camera
+ if(NETWORK_BUG,ADMIN_BUG)
+ if(length(list("SS13","MINE")&camera.network))
+ bugged_cameras[camera.c_tag] = camera
+ sortList(bugged_cameras)
+ return bugged_cameras
+
+
+/obj/item/device/camera_bug/proc/menu(var/list/cameras)
+ if(!cameras || !cameras.len)
+ return "No bugged cameras found."
+
+ var/html
+ switch(track_mode)
+ if(BUGMODE_LIST)
+ html = "Select a camera:
\[Cancel camera view\]
"
+ for(var/entry in cameras)
+ var/obj/machinery/camera/C = cameras[entry]
+ var/functions = ""
+ switch(bugtype)
+ if(SABOTAGE_BUG)
+ functions = " - \[Disable\]"
+ if(ADVANCED_BUG)
+ functions = " - \[Monitor\]"
+ if(ADMIN_BUG)
+ if(C.bug == src)
+ functions = " - \[Monitor\] \[Disable\]"
+ else
+ functions = " - \[Monitor\]"
+ html += "| [entry] | [functions] |
"
+
+ if(BUGMODE_MONITOR)
+ if(current)
+ html = "Analyzing Camera '[current.c_tag]' \[Select Camera\]
"
+ html += camera_report()
+ else
+ track_mode = BUGMODE_LIST
+ return .(cameras)
+ if(BUGMODE_TRACK)
+ if(tracking)
+ html = "Tracking '[tracked_name]' \[Cancel Tracking\] \[Cancel camera view\]
"
+ if(last_found)
+ var/time_diff = round((world.time - last_seen) / 150)
+ var/obj/machinery/camera/C = bugged_cameras[last_found]
+ var/outstring
+ if(C)
+ outstring = "[last_found]"
+ else
+ outstring = last_found
+ if(!time_diff)
+ html += "Last seen near [outstring] (now)
"
+ else
+ // 15 second intervals ~ 1/4 minute
+ var/m = round(time_diff/4)
+ var/s = (time_diff - 4*m) * 15
+ if(!s) s = "00"
+ html += "Last seen near [outstring] ([m]:[s] minute\s ago)
"
+ else
+ html += "Not yet seen."
+ else
+ track_mode = BUGMODE_LIST
+ return .(cameras)
+ return html
+
+/obj/item/device/camera_bug/proc/camera_report()
+ // this should only be called if current exists
+ var/dat = ""
+ if(current && current.can_use())
+ var/list/seen = current.can_see()
+ var/list/names = list()
+ for(var/obj/machinery/singularity/S in seen) // god help you if you see more than one
+ if(S.name in names)
+ names[S.name]++
+ dat += "[S.name] ([names[S.name]])"
+ else
+ names[S.name] = 1
+ dat += "[S.name]"
+ var/stage = round(S.current_size / 2)+1
+ dat += " (Stage [stage])"
+ dat += " \[Track\]
"
+
+ for(var/obj/mecha/M in seen)
+ if(M.name in names)
+ names[M.name]++
+ dat += "[M.name] ([names[M.name]])"
+ else
+ names[M.name] = 1
+ dat += "[M.name]"
+ dat += " \[Track\]
"
+
+
+ for(var/mob/living/M in seen)
+ if(M.name in names)
+ names[M.name]++
+ dat += "[M.name] ([names[M.name]])"
+ else
+ names[M.name] = 1
+ dat += "[M.name]"
+ if(M.buckled && !M.lying)
+ dat += " (Sitting)"
+ if(M.lying)
+ dat += " (Laying down)"
+ dat += " \[Track\]
"
+ if(length(dat) == 0)
+ dat += "No motion detected."
+ return dat
+ else
+ return "Camera Offline
"
+
+/obj/item/device/camera_bug/Topic(var/href,var/list/href_list)
+ if(usr != loc)
+ usr.unset_machine()
+ usr.reset_view(null)
+ usr << browse(null, "window=camerabug")
+ return
+ usr.set_machine(src)
+ if("mode" in href_list)
+ track_mode = text2num(href_list["mode"])
+ if("monitor" in href_list)
+ var/obj/machinery/camera/C = locate(href_list["monitor"])
+ if(C)
+ track_mode = BUGMODE_MONITOR
+ current = C
+ usr.reset_view(null)
+ interact()
+ if("track" in href_list)
+ var/atom/A = locate(href_list["track"])
+ if(A)
+ tracking = A
+ tracked_name = A.name
+ last_found = current.c_tag
+ last_seen = world.time
+ track_mode = BUGMODE_TRACK
+ if("emp" in href_list)
+ var/obj/machinery/camera/C = locate(href_list["emp"])
+ if(istype(C) && C.bug == src)
+ C.emp_act(1)
+ C.bug = null
+ bugged_cameras -= C.c_tag
+ interact()
+ return
+ if("close" in href_list)
+ usr.reset_view(null)
+ usr.unset_machine()
+ current = null
+ return // I do not <- I do not remember what I was going to write in this comment -Sayu, sometime later
+ if("view" in href_list)
+ var/obj/machinery/camera/C = locate(href_list["view"])
+ if(istype(C))
+ if(!C.can_use())
+ usr << "Something's wrong with that camera. You can't get a feed."
+ return
+ var/turf/T = get_turf(loc)
+ if(!T || C.z != T.z)
+ usr << "You can't get a signal."
+ return
+ current = C
+ spawn(6)
+ if(src.check_eye(usr))
+ usr.reset_view(C)
+ interact()
+ else
+ usr.unset_machine()
+ usr.reset_view(null)
+ usr << browse(null, "window=camerabug")
+ return
+ else
+ usr.unset_machine()
+ usr.reset_view(null)
+
+ interact()
+
+/obj/item/device/camera_bug/process()
+ if(track_mode == BUGMODE_LIST || (world.time < (last_tracked + refresh_interval)))
+ return
+ last_tracked = world.time
+ if(track_mode == BUGMODE_TRACK ) // search for user
+ // Note that it will be tricked if your name appears to change.
+ // This is not optimal but it is better than tracking you relentlessly despite everything.
+ if(!tracking)
+ src.updateSelfDialog()
+ return
+
+ if(tracking.name != tracked_name) // Hiding their identity, tricksy
+ var/mob/M = tracking
+ if(istype(M))
+ if(!(tracked_name == "Unknown" && findtext(tracking.name,"Unknown"))) // we saw then disguised before
+ if(!(tracked_name == M.real_name && findtext(tracking.name,M.real_name))) // or they're still ID'd
+ src.updateSelfDialog()//But if it's neither of those cases
+ return // you won't find em on the cameras
+ else
+ src.updateSelfDialog()
+ return
+
+ var/list/tracking_cams = list()
+ var/list/b_cams = get_cameras()
+ for(var/entry in b_cams)
+ tracking_cams += b_cams[entry]
+ var/list/target_region = view(tracking)
+
+ for(var/obj/machinery/camera/C in (target_region & tracking_cams))
+ if(!can_see(C,tracking)) // target may have xray, that doesn't make them visible to cameras
+ continue
+ if(C.can_use())
+ last_found = C.c_tag
+ last_seen = world.time
+ break
+ src.updateSelfDialog()
+
+/obj/item/device/camera_bug/attackby(var/obj/item/W as obj,var/mob/living/user as mob, params)
+ if(istype(W,/obj/item/weapon/screwdriver) && expansion)
+ expansion.loc = get_turf(loc)
+ user << "You unscrew [expansion]."
+ user.put_in_inactive_hand(expansion)
+ expansion = null
+ bugtype = VANILLA_BUG
+ skip_bugcheck = 0
+ track_mode = BUGMODE_LIST
+ tracking = null
+ return
+
+ if(expansion || !W)
+ return ..(W,user)
+
+ // I am not sure that this list is or should be final
+ // really I do not know what to do here.
+ var/static/list/expandables = list(
+ /obj/item/weapon/research = ADMIN_BUG, // could have been anything spawn-only
+
+ // these are all so hackish I am sorry
+
+ /obj/item/device/analyzer = UNIVERSAL_BUG,
+ /obj/item/weapon/stock_parts/subspace/analyzer = UNIVERSAL_BUG,
+
+ /obj/item/device/assembly/igniter = SABOTAGE_BUG,
+ /obj/item/device/assembly/infra = SABOTAGE_BUG, // ir blaster to disable camera
+ /obj/item/weapon/stock_parts/subspace/amplifier = SABOTAGE_BUG,
+
+ /obj/item/device/radio = NETWORK_BUG,
+ /obj/item/device/assembly/signaler = NETWORK_BUG,
+ /obj/item/weapon/stock_parts/subspace/transmitter = NETWORK_BUG,
+
+ /obj/item/device/detective_scanner = ADVANCED_BUG,
+ /obj/item/device/paicard = ADVANCED_BUG,
+ /obj/item/weapon/stock_parts/scanning_module = ADVANCED_BUG
+ )
+
+ for(var/entry in expandables)
+ if(istype(W,entry))
+ bugtype = expandables[entry]
+ user.drop_item()
+ W.loc = src
+ expansion = W
+ user << "You add [W] to [src]."
+ get_cameras() // the tracking code will want to know the new camera list
+ if(bugtype in list(UNIVERSAL_BUG,NETWORK_BUG,ADMIN_BUG))
+ skip_bugcheck = 1
+ return
+
+#undef VANILLA_BUG
+#undef UNIVERSAL_BUG
+#undef NETWORK_BUG
+#undef SABOTAGE_BUG
+#undef ADVANCED_BUG
+#undef ADMIN_BUG
+
+#undef BUGMODE_LIST
+#undef BUGMODE_MONITOR
+#undef BUGMODE_TRACK
\ No newline at end of file
diff --git a/code/game/objects/items/devices/handtv.dm b/code/game/objects/items/devices/handtv.dm
deleted file mode 100644
index 48e22348f32..00000000000
--- a/code/game/objects/items/devices/handtv.dm
+++ /dev/null
@@ -1,47 +0,0 @@
-/obj/item/device/handtv
- name = "handheld tv"
- desc = "A handheld tv meant for remote viewing."
- icon_state = "handtv"
- w_class = 1
- var/obj/machinery/camera/current = null
-
-/obj/item/device/handtv/attack_self(mob/usr as mob)
- var/list/cameras = new/list()
- for (var/obj/machinery/camera/C in cameranet.cameras)
- if (C.hasbug && C.status)
- cameras.Add(C)
- if (length(cameras) == 0)
- usr << "\red No bugged functioning cameras found."
- return
-
- var/list/friendly_cameras = new/list()
-
- for (var/obj/machinery/camera/C in cameras)
- friendly_cameras.Add(C.c_tag)
-
- var/target = input("Select the camera to observe", null) as null|anything in friendly_cameras
- if (!target)
- usr.unset_machine()
- usr.reset_view(usr)
- src.in_use = 0
- return
- for (var/obj/machinery/camera/C in cameras)
- if (C.c_tag == target)
- target = C
- break
- if (usr.stat == 2) return
- if(target)
- usr.client.eye = target
- usr.set_machine(src)
- src.current = target
- src.in_use = 1
- else
- usr.unset_machine()
- src.in_use = 0
- return
-
-/obj/item/device/handtv/check_eye(var/mob/usr as mob)
- if ( src.loc != usr || usr.get_active_hand() != src|| !usr.canmove || usr.blinded || !current || !current.status )
- return null
- usr.reset_view(current)
- return 1
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm
index 6798f0857c4..db7ffa49f5a 100644
--- a/code/game/objects/items/weapons/storage/boxes.dm
+++ b/code/game/objects/items/weapons/storage/boxes.dm
@@ -41,22 +41,6 @@
autoignition_temperature = 530 // Kelvin
-
-/obj/item/weapon/storage/box/surveillance
- name = "\improper DromedaryCo packet"
- desc = "A packet of six imported DromedaryCo cancer sticks. A label on the packaging reads, \"Wouldn't a slow death make a change?\""
- icon = 'icons/obj/cigarettes.dmi'
- icon_state = "Dpacket"
- item_state = "Dpacket"
- w_class = 1
- foldable = null
- New()
- ..()
- contents = list()
- sleep(1)
- for(var/i = 1 to 5)
- new /obj/item/device/camera_bug(src)
-
/obj/item/weapon/storage/box/survival
New()
..()
diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm
index 1d7e72fd1bc..710251b34d7 100644
--- a/code/game/objects/items/weapons/storage/uplink_kits.dm
+++ b/code/game/objects/items/weapons/storage/uplink_kits.dm
@@ -8,6 +8,7 @@
new /obj/item/weapon/card/id/syndicate(src)
new /obj/item/weapon/card/id/syndicate(src)
new /obj/item/clothing/shoes/syndigaloshes(src)
+ new /obj/item/device/camera_bug(src)
return
if("stealth")
@@ -134,15 +135,6 @@
new /obj/item/clothing/head/helmet/space/syndicate/black/red(src)
return
-/obj/item/weapon/storage/box/syndie_kit/surveillance
- name = "box (S)"
-
-/obj/item/weapon/storage/box/syndie_kit/surveillance/New()
- ..()
- new /obj/item/device/handtv(src)
- new /obj/item/weapon/storage/box/surveillance(src)
- return
-
/obj/item/weapon/storage/box/syndie_kit/conversion
name = "box (CK)"
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index e2d710e0bc5..d5805617b5c 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -83,6 +83,7 @@ var/list/ai_verbs_default = list(
var/apc_override = 0 //hack for letting the AI use its APC even when visionless
var/camera_light_on = 0 //Defines if the AI toggled the light on the camera it's looking through.
var/datum/trackable/track = null
+ var/last_paper_seen = null
var/can_shunt = 1
var/last_announcement = ""
var/obj/machinery/bot/Bot
@@ -98,8 +99,8 @@ var/list/ai_verbs_default = list(
src.verbs |= ai_verbs_default
/mob/living/silicon/ai/proc/remove_ai_verbs()
- src.verbs -= ai_verbs_default
-
+ src.verbs -= ai_verbs_default
+
/mob/living/silicon/ai/New(loc, var/datum/ai_laws/L, var/obj/item/device/mmi/B, var/safety = 0)
var/list/possibleNames = ai_names
@@ -145,7 +146,7 @@ var/list/ai_verbs_default = list(
add_language("Galactic Common", 1)
add_language("Sol Common", 1)
add_language("Tradeband", 1)
- add_language("Gutter", 0)
+ add_language("Gutter", 0)
add_language("Sinta'unathi", 0)
add_language("Siik'tajr", 0)
add_language("Skrellian", 0)
@@ -154,8 +155,8 @@ var/list/ai_verbs_default = list(
add_language("Trinary", 1)
add_language("Chittin", 0)
add_language("Bubblish", 0)
- add_language("Clownish", 0)
-
+ add_language("Clownish", 0)
+
if(!safety)//Only used by AIize() to successfully spawn an AI.
if (!B)//If there is no player/brain inside.
new/obj/structure/AIcore/deactivated(loc)//New empty terminal.
@@ -181,12 +182,12 @@ var/list/ai_verbs_default = list(
hud_list[SPECIALROLE_HUD] = image('icons/mob/hud.dmi', src, "hudblank")
hud_list[NATIONS_HUD] = image('icons/mob/hud.dmi', src, "hudblank")
- init_subsystems()
-
+ init_subsystems()
+
ai_list += src
..()
return
-
+
/mob/living/silicon/ai/proc/on_mob_init()
src << "You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras)."
src << "To look at other parts of the station, click on yourself to get a camera menu."
@@ -210,7 +211,7 @@ var/list/ai_verbs_default = list(
src << "These laws may be changed by other players, or by you being the traitor."
job = "AI"
-
+
/mob/living/silicon/ai/proc/SetName(pickedName as text)
real_name = pickedName
name = pickedName
@@ -325,7 +326,7 @@ var/list/ai_verbs_default = list(
/mob/living/silicon/ai/proc/ai_alerts()
set name = "Show Alerts"
set category = "AI Commands"
-
+
var/dat = "Current Station Alerts\n"
dat += "Close
"
for (var/cat in alarms)
@@ -371,15 +372,15 @@ var/list/ai_verbs_default = list(
if(src.stat == 2)
src << "You can't call the shuttle because you are dead!"
return
-
+
if(check_unable(AI_CHECK_WIRELESS))
- return
+ return
var/confirm = alert("Are you sure you want to call the shuttle?", "Confirm Shuttle Call", "Yes", "No")
if(check_unable(AI_CHECK_WIRELESS))
- return
-
+ return
+
if(confirm == "Yes")
call_shuttle_proc(src)
@@ -389,23 +390,23 @@ var/list/ai_verbs_default = list(
if(C)
C.post_status("shuttle")
return
-
+
/mob/living/silicon/ai/proc/ai_cancel_call()
set name = "Recall Emergency Shuttle"
set category = "AI Commands"
-
+
if(src.stat == 2)
src << "You can't send the shuttle back because you are dead!"
return
-
+
if(check_unable(AI_CHECK_WIRELESS))
return
- var/confirm = alert("Are you sure you want to recall the shuttle?", "Confirm Shuttle Recall", "Yes", "No")
-
+ var/confirm = alert("Are you sure you want to recall the shuttle?", "Confirm Shuttle Recall", "Yes", "No")
+
if(check_unable(AI_CHECK_WIRELESS))
- return
-
+ return
+
if(confirm == "Yes")
cancel_call_proc(src)
@@ -415,29 +416,29 @@ var/list/ai_verbs_default = list(
/mob/living/silicon/ai/verb/toggle_anchor()
set category = "AI Commands"
set name = "Toggle Floor Bolts"
-
+
if(!isturf(loc)) // if their location isn't a turf
return // stop
-
+
anchored = !anchored // Toggles the anchor
src << "[anchored ? "You are now anchored." : "You are now unanchored."]"
/mob/living/silicon/ai/update_canmove()
return 0
-
+
/mob/living/silicon/ai/proc/announcement()
set name = "Announcement"
set desc = "Create a vocal announcement by typing in the available words to create a sentence."
set category = "AI Commands"
-
+
if(src.stat == 2)
src << "You can't call make an announcement because you are dead!"
return
-
+
if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
- return
-
+ return
+
ai_announcement()
/mob/living/silicon/ai/check_eye(var/mob/user as mob)
@@ -499,6 +500,9 @@ var/list/ai_verbs_default = list(
switchCamera(locate(href_list["switchcamera"])) in cameranet.cameras
if (href_list["showalerts"])
ai_alerts()
+ if(href_list["show_paper"])
+ if(last_paper_seen)
+ src << browse(last_paper_seen, "window=show_paper")
//Carn: holopad requests
if (href_list["jumptoholopad"])
var/obj/machinery/hologram/holopad/H = locate(href_list["jumptoholopad"])
@@ -673,7 +677,7 @@ var/list/ai_verbs_default = list(
if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
return
-
+
var/ai_allowed_Zlevel = list(1,3,5)
var/d
var/area/bot_area
@@ -794,7 +798,7 @@ var/list/ai_verbs_default = list(
set name = "Jump To Network"
unset_machine()
var/cameralist[0]
-
+
if(check_unable())
return
@@ -814,7 +818,7 @@ var/list/ai_verbs_default = list(
cameralist[i] = i
var/old_network = network
network = input(U, "Which network would you like to view?") as null|anything in cameralist
-
+
if(check_unable())
return
@@ -848,16 +852,16 @@ var/list/ai_verbs_default = list(
if(usr.stat == 2)
usr <<"You cannot change your emotional status because you are dead!"
return
-
+
if(check_unable())
- return
-
+ return
+
var/list/ai_emotions = list("Very Happy", "Happy", "Neutral", "Unsure", "Confused", "Sad", "BSOD", "Blank", "Problems?", "Awesome", "Facepalm", "Friend Computer")
var/emote = input("Please, select a status!", "AI Status", null, null) in ai_emotions
-
+
if(check_unable())
- return
-
+ return
+
for (var/obj/machinery/M in machines) //change status
if(istype(M, /obj/machinery/ai_status_display))
var/obj/machinery/ai_status_display/AISD = M
@@ -877,7 +881,7 @@ var/list/ai_verbs_default = list(
set name = "Change Hologram"
set desc = "Change the default hologram available to AI to something else."
set category = "AI Commands"
-
+
if(check_unable())
return
@@ -934,7 +938,7 @@ var/list/ai_verbs_default = list(
if(stat != CONSCIOUS)
return
-
+
if(check_unable())
return
@@ -1014,11 +1018,11 @@ var/list/ai_verbs_default = list(
set name = "Radio Settings"
set desc = "Allows you to change settings of your radio."
set category = "AI Commands"
-
+
if(check_unable(AI_CHECK_RADIO))
- return
-
- src << "Accessing Subspace Transceiver control..."
+ return
+
+ src << "Accessing Subspace Transceiver control..."
if (src.aiRadio)
src.aiRadio.interact(src)
@@ -1070,7 +1074,7 @@ var/list/ai_verbs_default = list(
else
src << "\red You've failed to open an airlock for [target]"
return
-
+
/mob/living/silicon/ai/proc/check_unable(var/flags = 0)
if(stat == DEAD)
@@ -1087,7 +1091,7 @@ var/list/ai_verbs_default = list(
/mob/living/silicon/ai/proc/is_in_chassis()
return istype(loc, /turf)
-
+
#undef AI_CHECK_WIRELESS
#undef AI_CHECK_RADIO
-
+
diff --git a/icons/mob/in-hand/tools.dmi b/icons/mob/in-hand/tools.dmi
index 86bdbd965e5..61aba754e1b 100644
Binary files a/icons/mob/in-hand/tools.dmi and b/icons/mob/in-hand/tools.dmi differ
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index d226cf46949..6d1d9eb401f 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/maps/cyberiad.dmm b/maps/cyberiad.dmm
index a9bf362edc2..d48772168a7 100644
--- a/maps/cyberiad.dmm
+++ b/maps/cyberiad.dmm
@@ -7647,7 +7647,7 @@
"cRc" = (/obj/effect/decal/cleanable/cobweb2,/turf/unsimulated/floor{name = "plating"},/area/prison/solitary)
"cRd" = (/obj/structure/stool/bed,/turf/unsimulated/floor{icon_state = "floorscorched2"},/area/prison/solitary)
"cRe" = (/obj/effect/decal/cleanable/blood,/turf/unsimulated/floor{name = "plating"},/area/prison/solitary)
-"cRf" = (/obj/machinery/door_control{id = "alienshutters"; name = "Alien Blast Shutter Control"; pixel_x = -26; pixel_y = -2; req_access_txt = "0"},/obj/machinery/camera{c_tag = "Xeno Shuttle North"; dir = 4; indestructible = 1; network = list("Xeno")},/turf/simulated/floor/plating/airless,/area/xenos_station/start)
+"cRf" = (/obj/machinery/camera{c_tag = "Xeno Shuttle East"; dir = 8; network = list("Xeno")},/turf/simulated/floor/plating/airless,/area/xenos_station/start)
"cRg" = (/obj/machinery/computer/xenos_station,/turf/simulated/floor/plating/airless,/area/xenos_station/start)
"cRh" = (/turf/simulated/floor/plating/airless,/area/xenos_station/start)
"cRi" = (/turf/simulated/floor/holofloor{tag = "icon-carpet2-0 (EAST)"; icon_state = "carpet2-0"; dir = 4},/area/holodeck/source_theatre)
@@ -7714,8 +7714,8 @@
"cSr" = (/obj/structure/window/reinforced{dir = 1},/obj/structure/window/reinforced,/turf/unsimulated/wall{tag = "icon-iron12"; icon_state = "iron12"},/area)
"cSs" = (/turf/unsimulated/wall{tag = "icon-iron11"; icon_state = "iron11"},/area)
"cSt" = (/turf/space/transit/north/shuttlespace_ns15,/area/shuttle/escape/transit)
-"cSu" = (/obj/machinery/camera{c_tag = "Xeno Shuttle West"; dir = 4; indestructible = 1; network = list("Xeno")},/turf/simulated/floor/plating/airless,/area/xenos_station/start)
-"cSv" = (/obj/machinery/camera{c_tag = "Xeno Shuttle East"; dir = 8; indestructible = 1; network = list("Xeno")},/turf/simulated/floor/plating/airless,/area/xenos_station/start)
+"cSu" = (/obj/machinery/camera{c_tag = "Xeno Shuttle West"; dir = 4; network = list("Xeno")},/turf/simulated/floor/plating/airless,/area/xenos_station/start)
+"cSv" = (/obj/machinery/door_control{id = "alienshutters"; name = "Alien Blast Shutter Control"; pixel_x = -26; pixel_y = -2; req_access_txt = "0"},/obj/machinery/camera{c_tag = "Xeno Shuttle North"; dir = 4; network = list("Xeno")},/turf/simulated/floor/plating/airless,/area/xenos_station/start)
"cSw" = (/turf/simulated/floor/holofloor{tag = "icon-1 (NORTHEAST)"; icon_state = "1"; dir = 5},/area/holodeck/source_space)
"cSx" = (/turf/simulated/floor/holofloor{tag = "icon-17 (NORTHEAST)"; icon_state = "17"; dir = 5},/area/holodeck/source_space)
"cSy" = (/turf/simulated/floor/holofloor{tag = "icon-22 (NORTHEAST)"; icon_state = "22"; dir = 5},/area/holodeck/source_space)
@@ -7850,8 +7850,8 @@
"cUX" = (/obj/structure/rack,/obj/item/clothing/head/that{pixel_x = 4; pixel_y = 6},/obj/item/clothing/under/suit_jacket,/obj/item/clothing/suit/wcoat,/turf/unsimulated/floor{icon_state = "cafeteria"; dir = 2},/area/ninja/holding)
"cUY" = (/obj/item/weapon/beach_ball,/turf/unsimulated/beach/sand,/area/ninja/holding)
"cUZ" = (/obj/structure/cable,/obj/structure/cable{icon_state = "0-4"; d2 = 4},/obj/machinery/power/smes/magical{name = "alien power storage unit"},/turf/simulated/floor/greengrid/airless,/area/xenos_station/start)
-"cVa" = (/obj/machinery/camera{c_tag = "Xeno Shuttle South-West"; dir = 1; indestructible = 1; network = list("Xeno")},/obj/machinery/power/terminal{dir = 8},/turf/simulated/floor/greengrid/airless,/area/xenos_station/start)
-"cVb" = (/obj/machinery/camera{c_tag = "Xeno Shuttle South-East"; dir = 1; indestructible = 1; network = list("Xeno")},/obj/effect/landmark{name = "Xenos-Spawn"; pixel_x = -1},/turf/simulated/floor/plating/airless,/area/xenos_station/start)
+"cVa" = (/obj/machinery/camera{c_tag = "Xeno Shuttle South-East"; dir = 1; network = list("Xeno")},/obj/effect/landmark{name = "Xenos-Spawn"; pixel_x = -1},/turf/simulated/floor/plating/airless,/area/xenos_station/start)
+"cVb" = (/obj/machinery/camera{c_tag = "Xeno Shuttle South-West"; dir = 1; network = list("Xeno")},/obj/machinery/power/terminal{dir = 8},/turf/simulated/floor/greengrid/airless,/area/xenos_station/start)
"cVc" = (/obj/structure/flora/grass/brown,/turf/simulated/floor/holofloor{tag = "icon-snow"; icon_state = "snow"},/area/holodeck/source_snowfield)
"cVd" = (/turf/simulated/floor/holofloor{dir = 2; icon_state = "green"},/area/holodeck/source_basketball)
"cVe" = (/turf/simulated/floor/holofloor{icon_state = "sand"; name = "Soft sand"},/area/holodeck/source_beach)
@@ -12793,19 +12793,19 @@ cOlcPfcPdcPncPecPjcPhcPacPlcPmcPbcPicOZcPkcPgcPccPhcPjcPbcPdcPncPacPicPecPkcPmcO
cOtcPecPmcOZcPacPgcPdcPhcPbcPjcPkcPlcPccPncPicPfcPdcPgcPkcPmcOZcPhcPlcPacPncPjcOocPtcPucOxcOycOzcOAcOBcORcODcPocPpcPqcPrcPvcPscPtcPucOAcOBcOCcODcOBcOCcODcPocPpcPqcPrcPvcPscPtcOLcOTcOUcPecPmcOZcPacPgcPdcPhcPbcPjcPkcPlcPccPncPicPfcPdcPgcPkcPmcOZcPhcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcPxcPxcPYcPZcPxcPzcQacQacQacQacQacPzcQbcQbcQbcQbcQccPzcPEcPEcPEcPEcPEcPzcPFcPFcPFcPFcPFcPzcPGcPGcPGcPGcPGcPzcPScPTcPTcPTcPUcPK
cOwcPacPjcPccPhcPicPmcPdcPkcPgcPncQdcQecQfcQdcQdcPmcPicPncPjcPccPdcPbcPhcOZcPgcOkcPocPpcPqcPrcPvcPscPtcOLcOxcOycOzcOAcOBcOCcODcPocPpcPqcPrcPvcPscPtcPucOxcOycOzcOAcOBcOCcODcPocOFcOTcOUcPacPjcPccPhcPicPmcPdcPkcPgcPncPbcPfcOZcPlcPecPmcPicPncPjcPccPdcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQgcQgcQgcQgcQgcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcQhcPxcPxcPxcPxcPzcQicQjcQkcQjcQlcPzcQmcQncQocQpcQmcPzcPEcPEcPQcPEcPEcPzcPFcPFcPFcPFcPFcPzcPGcPGcPGcPGcPGcPzcPScPTcPTcPTcPUcPK
cOjcPhcPgcPfcPdcPlcPjcPmcPncPicQqcQecQrcQscQtcQucQdcPlcOZcPgcPfcPmcPkcPdcPccPicOqcOzcOAcOBcOCcODcPocPpcOGcPrcPvcPscPtcPucOxcOycOzcOAcOxcOycOzcOAcPpcPqcPrcPvcPscPtcPucOxcOycOzcOPcOTcOUcPhcPgcPfcPdcPlcPjcPmcPncPicOZcPkcPecPccPbcPfcPjcPlcOZcPgcPfcPmcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQgcQwcQxcQycQzcQwcQgcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcPxcQhcPxcPxcQAcPzcQBcQjcQkcQjcQCcPzcQmcQDcQEcQFcQmcPzcPEcPEcPQcPEcPEcPzcPFcPFcPFcPFcPFcPzcPGcPGcPGcPGcPGcPzcQGcPTcPTcPTcQHcPK
-cOmcPdcPicPecPmcPbcPgcPjcOZcQIcQJcQrcQscQtcQKcQKcQLcQMcPccPicPecPjcPncPmcPfcPlcOncPvcPscPtcPucOxcOycOzcOPcOBcOCcODcPocPpcPqcPrcPvcQNcQOcQPcQQcQRcQScOAcOBcOCcODcPocPpcPqcPrcPvcOJcOTcOUcPdcPicPecPmcPbcPgcPjcQqcQIcQTcQUcQVcQWcQXcPhcPgcPbcPccPicPecPjcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQYcQZcRacQvcQZcRbcQvcQZcRccQvcRdcRecQvcQZcRbcQvaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQgcQwcQwcRfcRgcRhcQwcQwcQgcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcPxcPLcQhcPxcPYcPzcQacQacQacQacQacPzcRicQDcQEcQFcRicPzcPEcPEcPEcPEcPEcPzcPFcPFcPFcPFcPFcPzcPGcPGcPGcPGcPGcPzcQGcPTcPTcPTcQHcPK
+cOmcPdcPicPecPmcPbcPgcPjcOZcQIcQJcQrcQscQtcQKcQKcQLcQMcPccPicPecPjcPncPmcPfcPlcOncPvcPscPtcPucOxcOycOzcOPcOBcOCcODcPocPpcPqcPrcPvcQNcQOcQPcQQcQRcQScOAcOBcOCcODcPocPpcPqcPrcPvcOJcOTcOUcPdcPicPecPmcPbcPgcPjcQqcQIcQTcQUcQVcQWcQXcPhcPgcPbcPccPicPecPjcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQYcQZcRacQvcQZcRbcQvcQZcRccQvcRdcRecQvcQZcRbcQvaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQgcQwcQwcSvcRgcRhcQwcQwcQgcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcPxcPLcQhcPxcPYcPzcQacQacQacQacQacPzcRicQDcQEcQFcRicPzcPEcPEcPEcPEcPEcPzcPFcPFcPFcPFcPFcPzcPGcPGcPGcPGcPGcPzcQGcPTcPTcPTcQHcPK
cOpcPmcPlcPacPjcPkcPicPgcPccRjcRkcRkcRlcRmcQrcRncRncQMcPfcPlcPacPgcOZcPjcPecPbcOvcPucOxcOycOzcOAcOBcOCcOScPocPpcPqcPrcPvcPscPtcPucQPcRocRpcRqcRrcRscODcPocPpcPqcPrcPvcPscPtcPucOMcOTcOUcPmcPlcPacPjcPkcPicQLcQMcRtcRtcRtcRtcRtcQUcRucPicPkcPfcPlcPacPgcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQvcRvcRwcQvcRvcRecQvcRvcRxcQvcRvcRbcQvcRvcRacQvaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcQwcRhcRhcRycRhcRhcQwcQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcPxcPxcPxcQhcPxcPzcRzcRAcRzcRAcRzcPzcRBcQDcQEcQFcRBcPzcPEcPEcPEcPEcPEcPzcPFcPFcPFcPFcPFcPzcPGcPGcPGcPGcPGcPzcQGcPTcPTcPTcQHcPK
cOocPjcPbcPhcPgcPncPlcPicPfcRCcRlcRlcRDcREcRFcQscQscRGcPecPbcPhcPicPccPgcPacPkcOscPqcPrcPvcPscPtcPucOxcONcOzcOAcOBcOCcODcPocPpcPqcRHcRIcRJcRKcRocRLcOycOzcOAcOBcOCcODcPocPpcPqcOHcOTcOUcPjcPbcPhcPgcPncQIcQMcRtcRtcRtcRtcRtcRtcRtcRMcQIcPmcPecPbcPhcPicOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcRhcRhcRhcRhcRhcRhcRhcQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcPxcRNcPxcPxcQhcPzcPNcPOcPNcPOcPNcPzcRBcQDcQEcQFcRBcPzcPEcPQcPEcPQcPEcPzcPFcPFcPFcPFcPFcPzcPGcPRcPGcPRcPGcPzcQGcPTcPTcPTcQHcPK
cOkcPgcPkcPdcPicOZcPbcPlcQVcQKcRncRncROcQtcRDcRlcRlcRncRPcPkcPdcPlcPfcPicPhcPncOicOAcOBcOCcODcPocPpcPqcOHcPvcPscPtcPucOxcOycOzcOAcQOcRJcRKcRocRpcRQcPrcPvcPscPtcPucOxcOycOzcOAcOQcOTcOUcPgcPkcPdcPicOZcRjcRtcRtcRtcRtcRtcRtcRtcRtcRtcRjcPjcPacPkcPdcPlcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQvcQZcRwcQvcRRcRacQvcQZcRacQvcRScRbcQvcQZcRecQvaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcRTcRhcRhcRhcRhcRhcRhcRhcRTcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcPYcPxcRUcPxcPLcPzcPAcPBcPAcPBcPAcPzcRVcRWcRXcRYcRVcPzcPEcPEcPEcPEcPEcPzcPFcPFcPFcPFcPFcPzcPGcPGcPGcPGcPGcPzcRZcSacSacSacSbcPK
cOqcPicPncPmcPlcPccQVcQVcSccQrcSdcSdcSecRmcROcRlcRlcRncRPcRPcSfcPbcPecPlcPdcOZcOrcPpcPqcPrcPvcPscPtcPucOMcOycOzcOAcOBcOCcODcPocPpcSgcShcSicSjcQOcSicOxcOycOzcOAcOBcOCcODcPocPpcOGcOTcOUcPicPncPmcPlcPccQXcRtcRtcRtcRtcRtcRtcRtcRtcRtcQqcPgcPhcPncPmcPbcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQvcRvcRacQvcRvcRbcQvcRvcRbcQvcRvcSkcQvcRvcSlcQvaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcSmcRhcRhcSncRhcRhcSocQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacSpcSqcSqcSqcSqcSqcQgcSqcSqcSqcSqcSqcQgcSqcSqcSqcSqcSqcQgcSrcSrcSrcSrcSrcQgcSrcSrcSrcSrcSrcQgcSrcSrcSrcSrcSrcQgcSrcSrcSrcSrcSrcSs
-cOncPlcOZcPjcPbcPfcSfcRncRkcStcRncRkcStcQscSecQscSecSdcREcRDcRucPkcPacPbcPmcPccOucOycOzcOAcOBcOCcODcPocOFcPqcPrcPvcPscPtcPucOxcOycOCcOAcOBcOCcODcPrcPpcPqcPrcPvcPscPtcPucOxcOycOOcOTcOUcPlcOZcPjcPbcPfcQUcRtcRtcRtcRtcRtcRtcRtcRtcRtcQTcPicPdcOZcPjcPkcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcSucRhcRhcRhcRhcRhcSvcQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcSwcSxcSycSwcSxcPzcSzcSzcSzcSzcSzcPzcSAcSAcSAcSAcSAcPzcSBcSBcSCcSDcSDcPzcSEcSEcSEcSEcSEcPzcSFcSGcSGcSGcSHcPzcSIcSJcSJcSJcSKcPK
+cOncPlcOZcPjcPbcPfcSfcRncRkcStcRncRkcStcQscSecQscSecSdcREcRDcRucPkcPacPbcPmcPccOucOycOzcOAcOBcOCcODcPocOFcPqcPrcPvcPscPtcPucOxcOycOCcOAcOBcOCcODcPrcPpcPqcPrcPvcPscPtcPucOxcOycOOcOTcOUcPlcOZcPjcPbcPfcQUcRtcRtcRtcRtcRtcRtcRtcRtcRtcQTcPicPdcOZcPjcPkcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvcQvaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcSucRhcRhcRhcRhcRhcRfcQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcSwcSxcSycSwcSxcPzcSzcSzcSzcSzcSzcPzcSAcSAcSAcSAcSAcPzcSBcSBcSCcSDcSDcPzcSEcSEcSEcSEcSEcPzcSFcSGcSGcSGcSHcPzcSIcSJcSJcSJcSKcPK
cOvcPbcPccPgcPkcPecRucSdcREcRFcSdcREcRFcRlcQKcRlcQKcStcQtcROcRMcPncPhcPkcPjcPfcOlcOCcODcPocPpcPqcPrcPvcOJcPtcPucOxcOycOzcOAcOBcOCcOzcPqcPrcPvcPscPocPscPtcPucOxcOycOzcOAcOBcOCcOScOTcOUcPbcPccPgcPkcPecQqcRtcRtcRtcRtcRtcRtcRtcRtcRtcQWcPlcPmcPccPgcPncOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcRhcRhcRhcQwcQwcRhcRhcQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcSxcSycSwcSxcSycPzcSzcSLcSzcSzcSzcPzcSAcSMcSNcSOcSAcPzcSPcSPcSQcSRcSRcPzcSEcSScSEcSTcSEcPzcSUcSVcSVcSVcSWcPzcSXcSYcSYcSYcSZcPK
cOscPkcPfcPicPncPacRMcStcQtcRDcStcQtcRDcRncQrcRncQrcRFcRmcSecTacOZcPdcPncPgcPecOtcPrcPvcPscPtcPucOxcOycOOcOAcOBcOCcODcPocPpcPqcPrcShcQNcTbcSgcTccQQcOzcOAcOBcOCcODcPocPpcPqcPrcOIcOTcOUcPkcPfcPicPncPacQTcRtcRtcRtcRtcRtcRtcRtcRtcRtcRCcPbcPjcPfcPicOZcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcRhcRhcRhcQwcTdcRhcRhcQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcSycSwcSxcSycSwcPzcSzcSzcSzcTecSzcPzcSAcTfcTfcTfcSAcPzcSPcTgcTgcTgcSRcPzcSEcSEcSEcSEcSEcPzcSUcSVcSVcSVcSWcPzcSXcSYcSYcSYcSZcPK
cOicPncPecPlcOZcPhcTacRFcRmcROcRFcRmcROcSdcRkcSdcRkcRDcQscQKcQLcPccPmcOZcPicPacOwcPscPtcPucOxcOycOzcOAcOQcOCcODcPocPpcPqcPrcPvcPscQQcThcTicTjcTkcTlcOBcOCcODcPocPpcPqcPrcPvcPscOKcOTcOUcPncPecPlcOZcPhcQWcRtcRtcRtcRtcRtcRtcRtcRtcRtcQVcPkcPgcPecPlcPccOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacTmcTmcTmcTmcTmcTncTncTocTpcTpcTqcTncTncTncTncTncTncTncTncTncTrcTncTncTnaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcRhcRhcRhcQwcTscRycTtcQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcSwcSxcSycSwcSxcPzcSzcSzcSzcSzcSzcPzcSAcSAcSAcSAcSAcPzcSPcSQcSQcSQcSRcPzcSEcSEcSEcSEcSEcPzcSUcSVcSVcSVcSWcPzcSXcSYcSYcSYcSZcPK
cOrcOZcPacPbcPccPdcQLcRDcQscSecRDcQscSecStcREcStcREcROcRlcQrcQMcPfcPjcPccPlcPhcOjcODcPocPpcPqcPrcPvcPscOKcPucOxcOycOzcOAcOBcOCcODcTucTvcTwcTxcTycTzcPtcPucOxcOycOzcOAcOBcOCcODcOEcOTcOUcOZcPacPbcPccPdcRCcRtcRtcRtcRtcRtcRtcRtcRtcRtcSfcPncPicPacPbcPfcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacTAcTBcTCcTBcTmcTncTDcTEcTFcTGcTGcTHcTIcTJcTKcTLcTMcTNcTOcTNcTNcTNcTPcTnaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcTQcTRcTRcQwcTscRycTtcQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcSxcSycSwcSxcSycPzcSzcSzcSzcTScSzcPzcSAcTTcTUcTVcSAcPzcTWcTXcTXcTXcTYcPzcSEcTZcSEcSEcSEcPzcUacUbcUbcUbcUccPzcSXcSYcSYcSYcSZcPK
cOucPccPhcPkcPfcPmcQMcROcRlcQKcROcRlcQKcRFcQtcRFcQtcSecRncRkcQIcPecPgcPfcPbcPdcOmcPvcPscPtcPucOxcOycOzcOPcOBcOCcODcPocPpcPqcPrcPvcSicUdcUecUfcTvcUgcOAcOBcOCcODcPocPpcPqcPrcPvcOJcOTcOUcPccPhcPkcPfcPmcQVcRtcRtcRtcRtcRtcRtcRtcRtcRtcRucOZcPlcPhcPkcPecOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacTmcTmcTmcTBcTBcTBcTmcTncTDcTEcTEcTEcTEcTEcUhcUicUicUicUjcTNcTNcTNcUkcTNcTNcTnaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcUlcTRcUmcQwcUncRycUocQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcSycSwcSxcSycSwcPzcSzcUpcSzcSzcSzcPzcSAcUqcUrcUscSAcPzcUtcUucUucUucUvcPzcSEcSEcSEcUwcSEcPzcUxcUycUycUycUzcPzcUAcSYcSYcSYcUBcPK
cOlcPfcPdcPncPecPjcQIcSecRncQrcSecRncQrcRDcRmcRDcRmcQKcSdcREcRjcPacPicPecPkcPmcOpcOzcOAcOBcOCcODcPocPpcOGcPrcPvcPscPtcPucOxcOycOzcQRcTucUCcQNcTbcSgcPqcPrcPvcPscPtcPucOxcOycOzcOPcOTcOUcPfcPdcPncPecPjcSfcRtcRtcRtcRtcRtcRtcRtcRtcRtcRMcPccPbcPdcPncPacOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacUDcUEcUFcTBcTBcTBcTmcTncUGcTEcUHcUIcUJcUKcULcUicUicUicUicTNcTNcUMcTNcUNcTOcTnaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcUOcUPcTRcQwcTscRycTtcQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcSwcSxcSycSwcSxcPzcSzcSzcSzcSzcSLcPzcSAcUqcUrcUscSAcPzcUQcSQcURcSQcUScPzcSEcSEcSEcSEcSEcPzcUTcSVcSVcSVcUUcPzcUAcSYcSYcSYcUBcPK
-cOtcPecPmcOZcPacPgcRjcQKcSdcRkcQKcSdcRkcROcQscROcQscQrcStcQtcQXcPhcPlcPacPncPjcOocPqcPrcPvcPscPtcPucOxcONcOzcOAcOBcOCcODcPocPpcPqcPrcPvcPscPtcPucOxcOycOzcOAcOBcOCcODcPocPpcPqcOHcOTcOUcPecPmcOZcPacPgcRucRtcRtcRtcRtcRtcRtcRtcRtcRtcTacPfcPkcPmcOZcPhcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacUVcUWcUEcTBcTBcTBcTmcTncUXcUicUjcUjcUjcUjcUjcUicUicUicUicTNcTNcTNcUYcTNcTNcTnaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcUZcVacTRcQwcTscVbcTtcQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcSxcSycSwcSxcSycPzcVccSzcSzcSzcSzcPzcSAcUqcUrcUscSAcPzcUQcVdcVdcVdcUScPzcVecVecVecVecVecPzcUTcSVcSVcSVcUUcPzcUAcSYcSYcSYcUBcPK
+cOtcPecPmcOZcPacPgcRjcQKcSdcRkcQKcSdcRkcROcQscROcQscQrcStcQtcQXcPhcPlcPacPncPjcOocPqcPrcPvcPscPtcPucOxcONcOzcOAcOBcOCcODcPocPpcPqcPrcPvcPscPtcPucOxcOycOzcOAcOBcOCcODcPocPpcPqcOHcOTcOUcPecPmcOZcPacPgcRucRtcRtcRtcRtcRtcRtcRtcRtcRtcTacPfcPkcPmcOZcPhcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacUVcUWcUEcTBcTBcTBcTmcTncUXcUicUjcUjcUjcUjcUjcUicUicUicUicTNcTNcTNcUYcTNcTNcTnaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcUZcVbcTRcQwcTscVacTtcQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcSxcSycSwcSxcSycPzcVccSzcSzcSzcSzcPzcSAcUqcUrcUscSAcPzcUQcVdcVdcVdcUScPzcVecVecVecVecVecPzcUTcSVcSVcSVcUUcPzcUAcSYcSYcSYcUBcPK
cOwcPacPjcPccPhcPicQXcQrcStcREcQrcStcREcSecRlcSecRlcRkcRFcRmcQUcPdcPbcPhcOZcPgcOkcOBcOCcODcPocPpcPqcPrcOIcPscPtcPucOxcOycOzcOAcOBcOCcODcPocPpcPqcPrcPvcPscPtcPucOxcOycOzcOAcOBcORcOTcOUcPacPjcPccPhcPicRMcRtcRtcRtcRtcRtcRtcRtcRtcRtcQLcPecPncPjcPccPdcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacTmcTmcTmcTmcTmcTmcTmcTncVfcUicUicUicUicUicUicUicUicUicUicVgcTNcTNcTNcTNcTNcTnaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcQwcQwcQwcQwcQwcQwcQwcQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcSycSwcSxcSycSwcPzcSzcTScSzcUpcSzcPzcSAcVhcVicVjcSAcPzcUQcUQcSQcUScUScPzcVkcVkcVkcVkcVkcPzcUTcSVcSVcSVcUUcPzcUAcSYcSYcSYcUBcPK
cOjcPhcPgcPfcPdcPlcQUcRkcRFcQtcRkcRFcQtcQKcRncQKcRncREcRDcQscQqcPmcPkcPdcPccPicOqcPucOxcOycOzcOAcOBcOCcOScPocPpcPqcPrcPvcPscPtcPucOxcOycOzcOAcOBcOCcODcPocPpcPqcPrcPvcPscPtcPucOMcOTcOUcPhcPgcPfcPdcPlcTacRtcRtcRtcRtcRtcRtcRtcRtcRtcQMcPacOZcPgcPfcPmcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacTncVfcUicUicUicUicUicVlcUicUicVlcUicVmcVncVncVncVocVncTnaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcVpcVpcVpcVpcVpcVpcVpcQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacPwcSwcSxcSycSwcSxcPzcSzcSzcSzcSzcVccPzcSAcSAcSAcSAcSAcPzcVqcVqcVrcVscVscPzcVtcVtcVtcVtcVtcPzcVucVvcVvcVvcVwcPzcVxcVycVycVycVzcPK
cOmcPdcPicPecPmcPbcQqcREcRDcRmcREcRDcRmcQrcSdcQrcSdcQtcROcRlcQTcPjcPncPmcPfcPlcOncPscPtcPucOxcOycOzcOAcOQcOCcODcPocPpcPqcPrcPvcPscPtcPucOxcOycOzcOAcOBcOCcODcPocPpcPqcPrcPvcPscOKcOTcOUcPdcPicPecPmcPbcQLcRtcRtcRtcRtcRtcRtcRtcRtcRtcQIcPhcPccPicPecPjcOUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacTncVAcUicUicUicUicUjcVBcUjcUjcVBcUjcVCcVDcVDcVDcVDcVDcTnaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacQgcQwcVEcVEcVEcVEcVEcVEcVEcQwcQgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacVFcVGcVGcVGcVGcVGcVHcVGcVGcVGcVGcVGcVHcVGcVGcVGcVGcVGcVHcVGcVGcVGcVGcVGcVHcVGcVGcVGcVGcVGcVHcVGcVGcVGcVGcVGcVHcVGcVGcVGcVGcVGcVI
diff --git a/paradise.dme b/paradise.dme
index 95f52d506e6..a8ad358efbf 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -649,11 +649,11 @@
#include "code\game\objects\items\trash.dm"
#include "code\game\objects\items\devices\aicard.dm"
#include "code\game\objects\items\devices\autopsy.dm"
+#include "code\game\objects\items\devices\camera_bug.dm"
#include "code\game\objects\items\devices\chameleonproj.dm"
#include "code\game\objects\items\devices\debugger.dm"
#include "code\game\objects\items\devices\flash.dm"
#include "code\game\objects\items\devices\flashlight.dm"
-#include "code\game\objects\items\devices\handtv.dm"
#include "code\game\objects\items\devices\laserpointer.dm"
#include "code\game\objects\items\devices\lightreplacer.dm"
#include "code\game\objects\items\devices\megaphone.dm"