mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-21 21:17:44 +01:00
a6be5d4607
## About The Pull Request Installing camera upgrades now consumes more power. Each form of upgrade, x-ray, EMP resistance, and motion sensors all increase the power usage as multipliers. (which means the more upgrades, the more severe the power drain) | Upgrade | Power Multiplier | | ------------- | ------------- | | X-ray | x10 | | Motion Sensor | x4 | | EMP Resistance | x1.25 | The default power consumption of an active camera is 20. So if you stack all the upgrades it costs 1K, which is the default power cost for most station machinery. The power costs are not applied for AI malf upgrades, so it doesn't give away their antag status. Also this fixes disabled cameras consuming power. ## Why It's Good For The Game Upgrading cameras should have a drawback where it requires more power. This gives engineering more reasons to boost power production. ## Changelog 🆑 add: Installing camera upgrades now consumes more power. Installing several upgrades onto the same camera has a power multiplier effect that stacks. fix: Fix disabled cameras consuming power /🆑
478 lines
16 KiB
Plaintext
478 lines
16 KiB
Plaintext
/// Active power use of a regular default camera
|
|
#define CAMERA_POWER_CONSUMPTION (BASE_MACHINE_ACTIVE_CONSUMPTION * 0.02)
|
|
/// Active power multiplier of the xray camera upgrade
|
|
#define XRAY_POWER_MOD (10)
|
|
/// Active power multiplier of the motion camera upgrade
|
|
#define MOTION_POWER_MOD (4)
|
|
/// Active power multiplier of the EMP camera upgrade
|
|
#define EMP_POWER_MOD (1.25)
|
|
|
|
/**
|
|
* Camera assembly frame
|
|
* Putting this on a wall will put a deconstructed camera machine on the wall.
|
|
*/
|
|
/obj/item/wallframe/camera
|
|
name = "camera assembly"
|
|
desc = "The basic construction for Nanotrasen-Always-Watching-You cameras."
|
|
icon = 'icons/obj/machines/camera.dmi'
|
|
icon_state = "cameracase"
|
|
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT)
|
|
result_path = /obj/machinery/camera/autoname/deconstructed
|
|
wall_external = TRUE
|
|
|
|
/obj/item/wallframe/camera/find_support_structure(atom/structure)
|
|
return istype(structure, /obj/structure/window) ? structure : ..()
|
|
|
|
/obj/machinery/camera
|
|
name = "security camera"
|
|
desc = "It's used to monitor rooms."
|
|
icon = 'icons/obj/machines/camera.dmi'
|
|
icon_state = "camera"
|
|
base_icon_state = "camera"
|
|
use_power = ACTIVE_POWER_USE
|
|
active_power_usage = CAMERA_POWER_CONSUMPTION
|
|
layer = WALL_OBJ_LAYER
|
|
resistance_flags = FIRE_PROOF
|
|
damage_deflection = 12
|
|
armor_type = /datum/armor/machinery_camera
|
|
max_integrity = 100
|
|
integrity_failure = 0.5
|
|
|
|
///An analyzer in the camera being used for x-ray upgrade.
|
|
var/obj/item/analyzer/xray_module
|
|
///used to keep from revealing malf AI upgrades for user facing isXRay() checks when they use Upgrade Camera Network ability
|
|
///will be false if the camera is upgraded with the proper parts.
|
|
var/malf_xray_firmware_active
|
|
///so the malf upgrade is restored when the normal upgrade part is removed.
|
|
var/malf_xray_firmware_present
|
|
///A sheet of plasma stored inside of the camera, giving it EMP protection.
|
|
var/obj/item/stack/sheet/mineral/plasma/emp_module
|
|
///used to keep from revealing malf AI upgrades for user facing isEmp() checks after they use Upgrade Camera Network ability
|
|
///will be false if the camera is upgraded with the proper parts.
|
|
var/malf_emp_firmware_active
|
|
///so the malf upgrade is restored when the normal upgrade part is removed.
|
|
var/malf_emp_firmware_present
|
|
|
|
///The current state of the camera's construction, all mapped in ones start off already built.
|
|
var/camera_construction_state = CAMERA_STATE_FINISHED
|
|
|
|
///Bitflag of upgrades this camera has: (CAMERA_UPGRADE_XRAY | CAMERA_UPGRADE_EMP_PROOF | CAMERA_UPGRADE_MOTION)
|
|
var/camera_upgrade_bitflags = NONE
|
|
|
|
///List of all networks that can see this camera through the security console.
|
|
var/list/network = list(CAMERANET_NETWORK_SS13)
|
|
///The tag the camera has, which is essentially its name to security camera consoles.
|
|
var/c_tag = null
|
|
///Boolean on whether the camera is activated, so can be seen on camera consoles or will just be static.
|
|
var/camera_enabled = TRUE
|
|
///Boolean for special cameras to bypass the random chance of being broken on roundstart.
|
|
var/start_active = FALSE
|
|
///The area this camera is built in, which we will add/remove ourselves to the list of cameras in that area from.
|
|
var/area/myarea = null
|
|
|
|
///The max range (and default range) the camera can see.
|
|
var/view_range = 7
|
|
///The short range the camera can see, if tampered with to be short-sighted.
|
|
var/short_range = 2
|
|
|
|
///Boolean on whether the camera's alarm is triggered.
|
|
var/alarm_on = FALSE
|
|
///How many times this camera has been EMP'ed consecutively, will reset back to 0 when fixed.
|
|
var/emped
|
|
///Boolean on whether the AI can even turn on this camera's light- borg cameras dont have one, for example.
|
|
var/internal_light = TRUE
|
|
///Number of AIs watching this camera with lights on, used for icons.
|
|
var/in_use_lights = 0
|
|
|
|
///Represents a signal source of camera alarms about movement or camera tampering
|
|
var/datum/alarm_handler/alarm_manager
|
|
///Proximity monitor associated with this atom, for motion sensitive cameras.
|
|
var/datum/proximity_monitor/proximity_monitor
|
|
|
|
/// A copy of the last paper object that was shown to this camera.
|
|
var/obj/item/paper/last_shown_paper
|
|
|
|
var/list/datum/weakref/localMotionTargets = list()
|
|
var/detectTime = 0
|
|
var/datum/motion_group/area_motion = null
|
|
var/alarm_delay = 30 // Don't forget, there's another 3 seconds in queueAlarm()
|
|
|
|
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera, 0)
|
|
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/autoname, 0)
|
|
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/autoname/motion, 0)
|
|
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/emp_proof, 0)
|
|
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/motion, 0)
|
|
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
|
|
|
|
/datum/armor/machinery_camera
|
|
melee = 50
|
|
bullet = 20
|
|
laser = 20
|
|
energy = 20
|
|
fire = 90
|
|
acid = 50
|
|
|
|
/obj/machinery/camera/Initialize(mapload)
|
|
. = ..()
|
|
|
|
for(var/network_name in network)
|
|
network -= network_name
|
|
network += LOWER_TEXT(network_name)
|
|
|
|
SScameras.cameras += src
|
|
|
|
myarea = get_room_area()
|
|
|
|
if(camera_enabled)
|
|
SScameras.add_camera_to_chunk(src)
|
|
LAZYADD(myarea.cameras, src)
|
|
#ifdef MAP_TEST
|
|
update_appearance()
|
|
#else
|
|
if(mapload && !start_active && is_station_level(z) && prob(3))
|
|
toggle_cam()
|
|
else //this is handled by toggle_camera, so no need to update it twice.
|
|
update_appearance()
|
|
#endif
|
|
alarm_manager = new(src)
|
|
if(mapload)
|
|
find_and_mount_on_atom(mark_for_late_init = TRUE)
|
|
|
|
/obj/machinery/camera/get_turfs_to_mount_on()
|
|
return list(get_step(src, dir))
|
|
|
|
/obj/machinery/camera/Destroy(force)
|
|
if(can_use())
|
|
toggle_cam(null, 0) //kick anyone viewing out and remove from the camera chunks
|
|
SScameras.remove_camera_from_chunk(src)
|
|
SScameras.cameras -= src
|
|
cancelCameraAlarm()
|
|
if(isarea(myarea))
|
|
LAZYREMOVE(myarea.cameras, src)
|
|
QDEL_NULL(alarm_manager)
|
|
QDEL_NULL(last_shown_paper)
|
|
QDEL_NULL(xray_module)
|
|
QDEL_NULL(emp_module)
|
|
QDEL_NULL(proximity_monitor)
|
|
return ..()
|
|
|
|
/obj/machinery/camera/connect_to_shuttle(mapload, obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
|
|
for(var/i in network)
|
|
network -= i
|
|
network += "[port.shuttle_id]_[i]"
|
|
|
|
/obj/machinery/camera/Exited(atom/movable/gone, direction)
|
|
. = ..()
|
|
if(gone == xray_module)
|
|
xray_module = null
|
|
update_appearance()
|
|
if(malf_xray_firmware_present)
|
|
malf_xray_firmware_active = malf_xray_firmware_present //re-enable firmware based upgrades after the part is removed.
|
|
removeXRay(malf_xray_firmware_present) //make sure we don't remove MALF upgrades.
|
|
|
|
else if(gone == emp_module)
|
|
emp_module = null
|
|
if(malf_emp_firmware_present)
|
|
malf_emp_firmware_active = malf_emp_firmware_present //re-enable firmware based upgrades after the part is removed.
|
|
removeEmpProof(malf_emp_firmware_present) //make sure we don't remove MALF upgrades
|
|
|
|
else if(gone == proximity_monitor)
|
|
emp_module = null
|
|
removeMotion()
|
|
|
|
/obj/machinery/camera/proc/create_prox_monitor()
|
|
if(!proximity_monitor)
|
|
proximity_monitor = new(src, 1)
|
|
RegisterSignal(proximity_monitor, COMSIG_QDELETING, PROC_REF(proximity_deleted))
|
|
|
|
/obj/machinery/camera/proc/proximity_deleted()
|
|
SIGNAL_HANDLER
|
|
proximity_monitor = null
|
|
|
|
/obj/machinery/camera/proc/set_area_motion(datum/motion_group/group)
|
|
area_motion = group
|
|
create_prox_monitor()
|
|
|
|
/obj/machinery/camera/examine(mob/user)
|
|
. = ..()
|
|
|
|
if(isEmpProof(TRUE)) //don't reveal it's upgraded if was done via MALF AI Upgrade Camera Network ability
|
|
. += span_info("It has electromagnetic interference shielding installed.")
|
|
else
|
|
. += span_info("It can be shielded against electromagnetic interference with some <b>plasma</b>.")
|
|
|
|
if(isXRay(TRUE)) //don't reveal it's upgraded if was done via MALF AI Upgrade Camera Network ability
|
|
. += span_info("It has an X-ray photodiode installed.")
|
|
else
|
|
. += span_info("It can be upgraded with an X-ray photodiode with an <b>analyzer</b>.")
|
|
|
|
if(isMotion())
|
|
. += span_info("It has a proximity sensor installed.")
|
|
else
|
|
. += span_info("It can be upgraded with a <b>proximity sensor</b>.")
|
|
|
|
if(!camera_enabled)
|
|
. += span_info("It's currently deactivated.")
|
|
if(!panel_open && powered())
|
|
. += span_notice("You'll need to open its maintenance panel with a <b>screwdriver</b> to turn it back on.")
|
|
|
|
if(panel_open)
|
|
. += span_info("Its maintenance panel is currently open.")
|
|
if(!camera_enabled && powered())
|
|
. += span_info("It can reactivated with <b>wirecutters</b>.")
|
|
|
|
/obj/machinery/camera/emp_act(severity, reset_time = 90 SECONDS)
|
|
. = ..()
|
|
if(!camera_enabled)
|
|
return
|
|
if(. & EMP_PROTECT_SELF)
|
|
return
|
|
if(!prob(150 / severity))
|
|
return
|
|
network = list()
|
|
SScameras.remove_camera_from_chunk(src)
|
|
set_machine_stat(machine_stat | EMPED)
|
|
set_light(0)
|
|
emped++ //Increase the number of consecutive EMP's
|
|
update_appearance()
|
|
calculate_active_power()
|
|
addtimer(CALLBACK(src, PROC_REF(post_emp_reset), emped, network), reset_time)
|
|
for(var/mob/M as anything in GLOB.player_list)
|
|
if (M.client?.eye == src)
|
|
M.reset_perspective(null)
|
|
to_chat(M, span_warning("The screen bursts into static!"))
|
|
|
|
/obj/machinery/camera/on_saboteur(datum/source, disrupt_duration)
|
|
. = ..()
|
|
//lasts twice as much so we don't have to constantly shoot cameras just to be S T E A L T H Y
|
|
emp_act(EMP_LIGHT, reset_time = disrupt_duration * 2)
|
|
return TRUE
|
|
|
|
/obj/machinery/camera/proc/post_emp_reset(thisemp, previous_network)
|
|
if(QDELETED(src))
|
|
return
|
|
triggerCameraAlarm() //camera alarm triggers even if multiple EMPs are in effect.
|
|
if(emped != thisemp) //Only fix it if the camera hasn't been EMP'd again
|
|
return
|
|
network = previous_network
|
|
set_machine_stat(machine_stat & ~EMPED)
|
|
update_appearance()
|
|
if(can_use())
|
|
SScameras.add_camera_to_chunk(src)
|
|
emped = 0 //Resets the consecutive EMP count
|
|
addtimer(CALLBACK(src, PROC_REF(cancelCameraAlarm)), 10 SECONDS)
|
|
calculate_active_power()
|
|
|
|
/obj/machinery/camera/attack_ai(mob/living/silicon/ai/user)
|
|
if (!istype(user))
|
|
return
|
|
if (!can_use())
|
|
return
|
|
user.switchCamera(src)
|
|
|
|
/obj/machinery/camera/proc/setViewRange(num = 7)
|
|
src.view_range = num
|
|
SScameras.update_visibility(src)
|
|
|
|
/obj/machinery/camera/proc/shock(mob/living/user)
|
|
if(!istype(user))
|
|
return
|
|
user.electrocute_act(10, src)
|
|
|
|
/obj/machinery/camera/singularity_pull(atom/singularity, current_size)
|
|
if (camera_enabled && current_size >= STAGE_FIVE) // If the singulo is strong enough to pull anchored objects and the camera is still active, turn off the camera as it gets ripped off the wall.
|
|
toggle_cam(null, 0)
|
|
return ..()
|
|
|
|
///Drops a specific upgrade and nulls it where necessary.
|
|
/obj/machinery/camera/proc/drop_upgrade(obj/item/upgrade_dropped)
|
|
upgrade_dropped.forceMove(drop_location())
|
|
if(upgrade_dropped == xray_module)
|
|
xray_module = null
|
|
if(malf_xray_firmware_present)
|
|
malf_xray_firmware_active = malf_xray_firmware_present //re-enable firmware based upgrades after the part is removed.
|
|
update_appearance()
|
|
|
|
else if(upgrade_dropped == emp_module)
|
|
emp_module = null
|
|
if(malf_emp_firmware_present)
|
|
malf_emp_firmware_active = malf_emp_firmware_present //re-enable firmware based upgrades after the part is removed.
|
|
|
|
else if(upgrade_dropped == proximity_monitor)
|
|
proximity_monitor = null
|
|
|
|
/obj/machinery/camera/run_atom_armor(damage_amount, damage_type, damage_flag = 0, attack_dir)
|
|
if(machine_stat & BROKEN)
|
|
return damage_amount
|
|
. = ..()
|
|
|
|
/obj/machinery/camera/atom_break(damage_flag)
|
|
if(!camera_enabled)
|
|
return
|
|
. = ..()
|
|
if(.)
|
|
triggerCameraAlarm()
|
|
toggle_cam(null, 0)
|
|
|
|
/obj/machinery/camera/on_deconstruction(disassembled)
|
|
if(!disassembled)
|
|
if(camera_construction_state >= CAMERA_STATE_WIRED)
|
|
new /obj/item/stack/cable_coil(drop_location(), 2)
|
|
new /obj/item/stack/sheet/iron(loc)
|
|
return
|
|
|
|
var/obj/item/wallframe/camera/dropped_cam = new(drop_location())
|
|
dropped_cam.update_integrity(dropped_cam.max_integrity * 0.5)
|
|
if(camera_construction_state >= CAMERA_STATE_WIRED)
|
|
new /obj/item/stack/cable_coil(drop_location(), 2)
|
|
if(xray_module)
|
|
drop_upgrade(xray_module)
|
|
if(emp_module)
|
|
drop_upgrade(emp_module)
|
|
if(proximity_monitor)
|
|
drop_upgrade(proximity_monitor)
|
|
|
|
/obj/machinery/camera/update_icon_state() //TO-DO: Make panel open states, xray camera, and indicator lights overlays instead.
|
|
var/xray_module
|
|
if(isXRay(TRUE))
|
|
xray_module = "xray"
|
|
|
|
if(!camera_enabled)
|
|
icon_state = "[xray_module][base_icon_state]_off"
|
|
return ..()
|
|
if(machine_stat & EMPED)
|
|
icon_state = "[xray_module][base_icon_state]_emp"
|
|
return ..()
|
|
icon_state = "[xray_module][base_icon_state][in_use_lights ? "_in_use" : ""]"
|
|
return ..()
|
|
|
|
/obj/machinery/camera/proc/toggle_cam(mob/user, displaymessage = TRUE)
|
|
camera_enabled = !camera_enabled
|
|
if(can_use())
|
|
SScameras.add_camera_to_chunk(src)
|
|
if (isturf(loc))
|
|
myarea = get_area(src)
|
|
LAZYADD(myarea.cameras, src)
|
|
else
|
|
myarea = null
|
|
else
|
|
set_light(0)
|
|
SScameras.remove_camera_from_chunk(src)
|
|
if (isarea(myarea))
|
|
LAZYREMOVE(myarea.cameras, src)
|
|
// We are not guarenteed that the camera will be on a turf. account for that
|
|
var/change_msg = "deactivates"
|
|
if(camera_enabled)
|
|
change_msg = "reactivates"
|
|
triggerCameraAlarm()
|
|
if(!QDELETED(src)) //We'll be doing it anyway in destroy
|
|
addtimer(CALLBACK(src, PROC_REF(cancelCameraAlarm)), 10 SECONDS)
|
|
if(displaymessage)
|
|
if(user)
|
|
visible_message(span_danger("[user] [change_msg] [src]!"))
|
|
add_hiddenprint(user)
|
|
else
|
|
visible_message(span_danger("\The [src] [change_msg]!"))
|
|
|
|
playsound(src, 'sound/items/tools/wirecutter.ogg', 100, TRUE)
|
|
update_appearance() //update Initialize() if you remove this.
|
|
calculate_active_power()
|
|
|
|
// 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 as anything in GLOB.player_list)
|
|
if (O.client?.eye == src)
|
|
O.reset_perspective(null)
|
|
to_chat(O, span_warning("The screen bursts into static!"))
|
|
|
|
/obj/machinery/camera/proc/triggerCameraAlarm()
|
|
alarm_on = TRUE
|
|
alarm_manager.send_alarm(ALARM_CAMERA, src, src)
|
|
|
|
/obj/machinery/camera/proc/cancelCameraAlarm()
|
|
alarm_on = FALSE
|
|
alarm_manager.clear_alarm(ALARM_CAMERA)
|
|
|
|
/obj/machinery/camera/proc/can_use()
|
|
if(!camera_enabled)
|
|
return FALSE
|
|
if(machine_stat & EMPED)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/// Returns a list of turfs in this camera's view.
|
|
/// This includes turfs that are "obscured by darkness" from the camera's POV.
|
|
/obj/machinery/camera/proc/can_see()
|
|
var/list/see = null
|
|
var/turf/pos = get_turf(src)
|
|
var/turf/directly_above = GET_TURF_ABOVE(pos)
|
|
var/check_lower = pos != get_lowest_turf(pos)
|
|
var/check_higher = directly_above && istransparentturf(directly_above) && (pos != get_highest_turf(pos))
|
|
|
|
if(isXRay())
|
|
see = RANGE_TURFS(view_range, pos)
|
|
else
|
|
see = get_hear_turfs(view_range, pos)
|
|
|
|
if(check_lower || check_higher)
|
|
// Haha datum var access KILL ME
|
|
for(var/turf/seen as anything in see)
|
|
if(check_lower)
|
|
var/turf/below = GET_TURF_BELOW(seen)
|
|
while(below && istransparentturf(below))
|
|
see += RANGE_TURFS(1, below)
|
|
below = GET_TURF_BELOW(below)
|
|
if(check_higher)
|
|
var/turf/above = GET_TURF_ABOVE(seen)
|
|
while(above && istransparentturf(above))
|
|
see += RANGE_TURFS(1, above)
|
|
above = GET_TURF_ABOVE(above)
|
|
return see
|
|
|
|
/obj/machinery/camera/proc/Togglelight(on=0)
|
|
for(var/mob/living/silicon/ai/A in GLOB.ai_list)
|
|
for(var/obj/machinery/camera/cam in A.lit_cameras)
|
|
if(cam == src)
|
|
return
|
|
if(on)
|
|
set_light(AI_CAMERA_LUMINOSITY)
|
|
else
|
|
set_light(0)
|
|
|
|
/obj/machinery/camera/get_remote_view_fullscreens(mob/user)
|
|
if(view_range == short_range) //unfocused
|
|
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2)
|
|
|
|
/obj/machinery/camera/update_remote_sight(mob/living/user)
|
|
user.set_invis_see(SEE_INVISIBLE_LIVING) //can't see ghosts through cameras
|
|
if(isXRay())
|
|
user.add_sight(SEE_TURFS|SEE_MOBS|SEE_OBJS)
|
|
else
|
|
user.clear_sight(SEE_TURFS|SEE_MOBS|SEE_OBJS)
|
|
return TRUE
|
|
|
|
///Called when the camera starts being watched on a camera console.
|
|
/obj/machinery/camera/proc/on_start_watching()
|
|
return
|
|
|
|
///Called when the camera stops being watched on a camera console.
|
|
/obj/machinery/camera/proc/on_stop_watching()
|
|
return
|
|
|
|
/obj/machinery/camera/proc/calculate_active_power()
|
|
if(!can_use())
|
|
active_power_usage = 0
|
|
return
|
|
|
|
var/xray_power_mod = (camera_upgrade_bitflags & CAMERA_UPGRADE_XRAY) && !malf_xray_firmware_present ? XRAY_POWER_MOD : 1
|
|
var/motion_power_mod = (camera_upgrade_bitflags & CAMERA_UPGRADE_MOTION) ? MOTION_POWER_MOD : 1
|
|
var/EMP_power_mod = (camera_upgrade_bitflags & CAMERA_UPGRADE_EMP_PROOF) && !malf_emp_firmware_present ? EMP_POWER_MOD : 1
|
|
|
|
active_power_usage = CAMERA_POWER_CONSUMPTION * xray_power_mod * motion_power_mod * EMP_power_mod
|
|
|
|
#undef CAMERA_POWER_CONSUMPTION
|
|
#undef XRAY_POWER_MOD
|
|
#undef MOTION_POWER_MOD
|
|
#undef EMP_POWER_MOD
|