mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-27 14:07:51 +01:00
Port -tg-station cameranets
This should make it much easier to port abductors, if we ever want to do that. Changes: - Fixed a bug where the AI could not delete photos it takes. - Ported -tg- cameranets. This means that all of our camera based systems are more or less up to date with -tg-; 510 features are missing. - An AI with the 'camera lights' mode on will now, instead of the button toggling the closest camera's light, toggle camera lights on/off as the AI moves. - It now takes a minimum of 30 deciseconds between attempting to track someone, and locking on. This number is increased by their distance away from the AI eye. - The camera activate/deactivate proc is now called 'toggle_cam'. - The 'trackable' proc has been replaced by a more object-oriented 'mob.can_track()' proc. - The 'networks' section of the security camera console is now above the camera list. In laymans terms: This refactors how security cameras calculate what viewers can see, and adds a few neat features on top.
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
|
||||
//supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a
|
||||
#define RANGE_TURFS(RADIUS, CENTER) \
|
||||
block( \
|
||||
locate(max(CENTER.x-(RADIUS),1), max(CENTER.y-(RADIUS),1), CENTER.z), \
|
||||
locate(min(CENTER.x+(RADIUS),world.maxx), min(CENTER.y+(RADIUS),world.maxy), CENTER.z) \
|
||||
)
|
||||
|
||||
/proc/dopage(src,target)
|
||||
var/href_list
|
||||
|
||||
@@ -1664,6 +1664,25 @@ var/mob/dview/dview_mob = new
|
||||
|
||||
return I
|
||||
|
||||
//ultra range (no limitations on distance, faster than range for distances > 8); including areas drastically decreases performance
|
||||
/proc/urange(dist=0, atom/center=usr, orange=0, areas=0)
|
||||
if(!dist)
|
||||
if(!orange)
|
||||
return list(center)
|
||||
else
|
||||
return list()
|
||||
|
||||
var/list/turfs = RANGE_TURFS(dist, center)
|
||||
if(orange)
|
||||
turfs -= get_turf(center)
|
||||
. = list()
|
||||
for(var/V in turfs)
|
||||
var/turf/T = V
|
||||
. += T
|
||||
. += T.contents
|
||||
if(areas)
|
||||
. |= T.loc
|
||||
|
||||
/proc/turf_clear(turf/T)
|
||||
for(var/atom/A in T)
|
||||
if(A.simulated)
|
||||
|
||||
@@ -15,10 +15,9 @@
|
||||
icon_state = "camera"
|
||||
|
||||
/obj/screen/ai/camera_list/Click()
|
||||
if(isAI(usr))
|
||||
var/mob/living/silicon/ai/AI = usr
|
||||
var/camera = input(AI) in AI.get_camera_list()
|
||||
AI.ai_camera_list(camera)
|
||||
var/mob/living/silicon/ai/AI = usr
|
||||
var/camera = input(AI, "Choose which camera you want to view", "Cameras") as null|anything in AI.get_camera_list()
|
||||
AI.ai_camera_list(camera)
|
||||
|
||||
/obj/screen/ai/camera_track
|
||||
name = "Track With Camera"
|
||||
|
||||
@@ -38,7 +38,7 @@ var/const/CAMERA_WIRE_NOTHING2 = 32
|
||||
|
||||
if(CAMERA_WIRE_POWER)
|
||||
if(C.status && !mended || !C.status && mended)
|
||||
C.deactivate(usr, 1)
|
||||
C.toggle_cam(usr, 1)
|
||||
|
||||
if(CAMERA_WIRE_LIGHT)
|
||||
C.light_disabled = !mended
|
||||
@@ -60,7 +60,7 @@ var/const/CAMERA_WIRE_NOTHING2 = 32
|
||||
C.setViewRange(new_range)
|
||||
|
||||
if(CAMERA_WIRE_POWER)
|
||||
C.deactivate(null) // Deactivate the camera
|
||||
C.toggle_cam(null) // Deactivate the camera
|
||||
|
||||
if(CAMERA_WIRE_LIGHT)
|
||||
C.light_disabled = !C.light_disabled
|
||||
|
||||
@@ -49,7 +49,7 @@ var/const/BORG_WIRE_LAWCHECK = 16 // Not used on MoMMIs
|
||||
if (BORG_WIRE_CAMERA)
|
||||
if(!isnull(R.camera) && !R.scrambledcodes)
|
||||
R.camera.status = mended
|
||||
R.camera.deactivate(usr, 0) // Will kick anyone who is watching the Cyborg's camera.
|
||||
R.camera.toggle_cam(usr, 0) // Will kick anyone who is watching the Cyborg's camera.
|
||||
|
||||
if(BORG_WIRE_LAWCHECK) //Forces a law update if the borg is set to receive them. Since an update would happen when the borg checks its laws anyway, not much use, but eh
|
||||
if (R.lawupdate)
|
||||
@@ -70,7 +70,7 @@ var/const/BORG_WIRE_LAWCHECK = 16 // Not used on MoMMIs
|
||||
|
||||
if (BORG_WIRE_CAMERA)
|
||||
if(!isnull(R.camera) && R.camera.can_use() && !R.scrambledcodes)
|
||||
R.camera.deactivate(usr, 0) // Kick anyone watching the Cyborg's camera, doesn't display you disconnecting the camera.
|
||||
R.camera.toggle_cam(usr, 0) // Kick anyone watching the Cyborg's camera, doesn't display you disconnecting the camera.
|
||||
R.visible_message("[R]'s camera lense focuses loudly.")
|
||||
R << "Your camera lense focuses loudly."
|
||||
|
||||
|
||||
@@ -386,7 +386,7 @@ rcd light flash thingy on matter drain
|
||||
var/initial_range = initial(C.view_range) //To prevent calling the proc twice
|
||||
if(camera.uses > 0)
|
||||
if(!C.status)
|
||||
C.deactivate(src, 1) //Reactivates the camera based on status. Badly named proc.
|
||||
C.toggle_cam(src, 1) //Reactivates the camera based on status. Badly named proc.
|
||||
fixedcams++
|
||||
camera.uses--
|
||||
if(C.view_range != initial_range)
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
|
||||
/obj/machinery/camera/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S.DisIntegrate(src)
|
||||
deactivate(S, 0)
|
||||
toggle_cam(S, 0)
|
||||
|
||||
/obj/machinery/particle_accelerator/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>Disrupting the power grid would bring no benefit to us. Aborting.</span>"
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
var/list/network = list("SS13")
|
||||
var/c_tag = null
|
||||
var/c_tag_order = 999
|
||||
var/status = 1.0
|
||||
anchored = 1.0
|
||||
var/status = 1
|
||||
anchored = 1
|
||||
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
|
||||
@@ -38,32 +38,15 @@
|
||||
assembly.anchored = 1
|
||||
assembly.update_icon()
|
||||
|
||||
invalidateCameraCache()
|
||||
|
||||
if(cameranet.cameras_unsorted || !ticker)
|
||||
cameranet.cameras += src
|
||||
cameranet.cameras_unsorted = 1
|
||||
else
|
||||
dd_insertObjectList(cameranet.cameras, src)
|
||||
|
||||
var/list/open_networks = difflist(network,restricted_camera_networks) //...but if all of camera's networks are restricted, it only works for specific camera consoles.
|
||||
if(open_networks.len) //If there is at least one open network, chunk is available for AI usage.
|
||||
cameranet.addCamera(src)
|
||||
|
||||
/* // Use this to look for cameras that have the same c_tag.
|
||||
for(var/obj/machinery/camera/C in cameranet.cameras)
|
||||
var/list/tempnetwork = C.network&src.network
|
||||
if(C != src && C.c_tag == src.c_tag && tempnetwork.len)
|
||||
log_to_dd("[src.c_tag] [src.x] [src.y] [src.z] conflicts with [C.c_tag] [C.x] [C.y] [C.z]")
|
||||
*/
|
||||
cameranet.cameras += src
|
||||
cameranet.addCamera(src)
|
||||
|
||||
/obj/machinery/camera/initialize()
|
||||
if(z == ZLEVEL_STATION && prob(3) && !start_active)
|
||||
deactivate()
|
||||
toggle_cam()
|
||||
|
||||
/obj/machinery/camera/Destroy()
|
||||
invalidateCameraCache()
|
||||
deactivate(null, 0) //kick anyone viewing out
|
||||
toggle_cam(null, 0) //kick anyone viewing out
|
||||
if(assembly)
|
||||
qdel(assembly)
|
||||
assembly = null
|
||||
@@ -76,26 +59,25 @@
|
||||
wires = null
|
||||
cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that
|
||||
cameranet.cameras -= src
|
||||
var/list/open_networks = difflist(network,restricted_camera_networks)
|
||||
if(open_networks.len)
|
||||
cameranet.removeCamera(src)
|
||||
cameranet.removeCamera(src)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/camera/emp_act(severity)
|
||||
if(!status)
|
||||
return
|
||||
if(!isEmpProof())
|
||||
if(prob(100/severity))
|
||||
invalidateCameraCache()
|
||||
if(prob(150/severity))
|
||||
icon_state = "[initial(icon_state)]emp"
|
||||
var/list/previous_network = network
|
||||
network = list()
|
||||
cameranet.removeCamera(src)
|
||||
stat |= EMPED
|
||||
set_light(0)
|
||||
triggerCameraAlarm(10 * severity)
|
||||
emped = emped+1 //Increase the number of consecutive EMP's
|
||||
var/thisemp = emped //Take note of which EMP this proc is for
|
||||
spawn(900)
|
||||
if(loc) //qdel limbo
|
||||
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
|
||||
network = previous_network
|
||||
icon_state = initial(icon_state)
|
||||
@@ -104,7 +86,9 @@
|
||||
if(can_use())
|
||||
cameranet.addCamera(src)
|
||||
emped = 0 //Resets the consecutive EMP count
|
||||
invalidateCameraCache()
|
||||
spawn(100)
|
||||
if(!qdeleted(src))
|
||||
cancelCameraAlarm()
|
||||
for(var/mob/O in mob_list)
|
||||
if (O.client && O.client.eye == src)
|
||||
O.unset_machine()
|
||||
@@ -128,19 +112,18 @@
|
||||
if(!istype(user))
|
||||
return
|
||||
user.do_attack_animation(src)
|
||||
add_hiddenprint(user)
|
||||
status = 0
|
||||
visible_message("<span class='warning'>\The [user] slashes at [src]!</span>")
|
||||
playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1)
|
||||
icon_state = "[initial(icon_state)]1"
|
||||
add_hiddenprint(user)
|
||||
deactivate(user,0)
|
||||
toggle_cam(user, 0)
|
||||
|
||||
/obj/machinery/camera/proc/setViewRange(var/num = 7)
|
||||
|
||||
/obj/machinery/camera/proc/setViewRange(num = 7)
|
||||
src.view_range = num
|
||||
cameranet.updateVisibility(src, 0)
|
||||
|
||||
/obj/machinery/camera/attackby(W as obj, mob/living/user as mob, params)
|
||||
invalidateCameraCache()
|
||||
var/msg = "<span class='notice'>You attach [W] into the assembly inner circuits.</span>"
|
||||
var/msg2 = "<span class='notice'>The camera already has that upgrade!</span>"
|
||||
|
||||
@@ -214,12 +197,17 @@
|
||||
for(var/mob/O in player_list)
|
||||
if(istype(O, /mob/living/silicon/ai))
|
||||
var/mob/living/silicon/ai/AI = O
|
||||
if(U.name == "Unknown") AI << "<b>[U]</b> holds <a href='?_src_=usr;show_paper=1;'>\a [itemname]</a> up to one of your cameras ..."
|
||||
else AI << "<b><a href='byond://?src=\ref[O];track=\ref[U]'>[U]</a></b> holds <a href='?_src_=usr;show_paper=1;'>\a [itemname]</a> up to one of your cameras ..."
|
||||
if(AI.control_disabled || (AI.stat == DEAD))
|
||||
return
|
||||
if(U.name == "Unknown")
|
||||
AI << "<b>[U]</b> holds <a href='?_src_=usr;show_paper=1;'>\a [itemname]</a> up to one of your cameras ..."
|
||||
else
|
||||
AI << "<b><a href='?src=\ref[AI];track=[html_encode(U.name)]'>[U]</a></b> holds <a href='?_src_=usr;show_paper=1;'>\a [itemname]</a> up to one of your cameras ..."
|
||||
AI.last_paper_seen = "<HTML><HEAD><TITLE>[itemname]</TITLE></HEAD><BODY><TT>[info]</TT></BODY></HTML>"
|
||||
else if (O.client && O.client.eye == src)
|
||||
O << "[U] holds \a [itemname] up to one of the cameras ..."
|
||||
O << browse(text("<HTML><HEAD><TITLE>[]</TITLE></HEAD><BODY><TT>[]</TT></BODY></HTML>", itemname, info), text("window=[]", itemname))
|
||||
|
||||
else if (istype(W, /obj/item/device/camera_bug))
|
||||
if (!src.can_use())
|
||||
user << "<span class='notice'>Camera non-functional.</span>"
|
||||
@@ -232,8 +220,9 @@
|
||||
user << "<span class='notice'>Camera bugged.</span>"
|
||||
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.
|
||||
toggle_cam(user, 1)
|
||||
var/datum/effect/system/spark_spread/spark_system = new /datum/effect/system/spark_spread()
|
||||
spark_system.set_up(5, 0, loc)
|
||||
spark_system.start()
|
||||
@@ -241,6 +230,7 @@
|
||||
playsound(loc, "sparks", 50, 1)
|
||||
visible_message("<span class='notice'>[user] has sliced the camera apart with an energy blade!</span>")
|
||||
qdel(src)
|
||||
|
||||
else if(istype(W, /obj/item/device/laser_pointer))
|
||||
var/obj/item/device/laser_pointer/L = W
|
||||
L.laser_act(src, user)
|
||||
@@ -248,32 +238,32 @@
|
||||
..()
|
||||
return
|
||||
|
||||
/obj/machinery/camera/proc/deactivate(user as mob, var/choice = 1)
|
||||
/obj/machinery/camera/proc/toggle_cam(mob/user, displaymessage = 1)
|
||||
status = !status
|
||||
if(can_use())
|
||||
cameranet.addCamera(src)
|
||||
else
|
||||
set_light(0)
|
||||
cameranet.removeCamera(src)
|
||||
if(choice==1)
|
||||
invalidateCameraCache()
|
||||
status = !( src.status )
|
||||
if (!(src.status))
|
||||
if(user)
|
||||
visible_message("<span class='danger'>[user] deactivates [src]!</span>")
|
||||
add_hiddenprint(user)
|
||||
else
|
||||
visible_message("<span class='danger'>\The [src] deactivates!</span>")
|
||||
playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)
|
||||
icon_state = "[initial(icon_state)]1"
|
||||
|
||||
cameranet.updateChunk(x, y, z)
|
||||
var/change_msg = "deactivates"
|
||||
if(!status)
|
||||
icon_state = "[initial(icon_state)]1"
|
||||
else
|
||||
icon_state = initial(icon_state)
|
||||
change_msg = "reactivates"
|
||||
triggerCameraAlarm()
|
||||
spawn(100)
|
||||
if(!qdeleted(src))
|
||||
cancelCameraAlarm()
|
||||
if(displaymessage)
|
||||
if(user)
|
||||
visible_message("<span class='danger'>[user] [change_msg] [src]!</span>")
|
||||
add_hiddenprint(user)
|
||||
else
|
||||
if(user)
|
||||
visible_message("<span class='danger'>[user] reactivates [src]!</span>")
|
||||
add_hiddenprint(user)
|
||||
else
|
||||
visible_message("<span class='danger'>\The [src] reactivates!</span>")
|
||||
playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)
|
||||
icon_state = initial(icon_state)
|
||||
visible_message("<span class='danger'>\The [src] [change_msg]!</span>")
|
||||
|
||||
playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)
|
||||
|
||||
// now disconnect anyone using the camera
|
||||
//Apparently, this will disconnect anyone even if the camera was re-activated.
|
||||
@@ -286,14 +276,14 @@
|
||||
|
||||
/obj/machinery/camera/proc/triggerCameraAlarm(var/duration = 0)
|
||||
alarm_on = 1
|
||||
camera_alarm.triggerAlarm(loc, src, duration)
|
||||
motion_alarm.triggerAlarm(loc, src)
|
||||
|
||||
/obj/machinery/camera/proc/cancelCameraAlarm()
|
||||
if(wires.IsIndexCut(CAMERA_WIRE_ALARM))
|
||||
return
|
||||
|
||||
alarm_on = 0
|
||||
camera_alarm.clearAlarm(loc, src)
|
||||
motion_alarm.clearAlarm(loc, src)
|
||||
|
||||
/obj/machinery/camera/proc/can_use()
|
||||
if(!status)
|
||||
@@ -339,7 +329,6 @@
|
||||
return null
|
||||
|
||||
/proc/near_range_camera(var/mob/M)
|
||||
|
||||
for(var/obj/machinery/camera/C in range(4, M))
|
||||
if(C.can_use()) // check if camera disabled
|
||||
return C
|
||||
@@ -348,16 +337,13 @@
|
||||
return null
|
||||
|
||||
/obj/machinery/camera/proc/weld(var/obj/item/weapon/weldingtool/WT, var/mob/user)
|
||||
|
||||
if(busy)
|
||||
return 0
|
||||
if(!WT.isOn())
|
||||
if(!WT.remove_fuel(0, user))
|
||||
return 0
|
||||
|
||||
// Do after stuff here
|
||||
user << "<span class='notice'>You start to weld [src].</span>"
|
||||
user << "<span class='notice'>You start to weld [src]...</span>"
|
||||
playsound(src.loc, 'sound/items/Welder.ogg', 50, 1)
|
||||
WT.eyecheck(user)
|
||||
busy = 1
|
||||
if(do_after(user, 100, target = src))
|
||||
busy = 0
|
||||
@@ -367,12 +353,37 @@
|
||||
busy = 0
|
||||
return 0
|
||||
|
||||
/obj/machinery/camera/proc/Togglelight(on=0)
|
||||
for(var/mob/living/silicon/ai/A in ai_list)
|
||||
for(var/obj/machinery/camera/cam in A.lit_cameras)
|
||||
if(cam == src)
|
||||
return
|
||||
if(on)
|
||||
src.set_light(AI_CAMERA_LUMINOSITY)
|
||||
else
|
||||
src.set_light(0)
|
||||
|
||||
/obj/machinery/camera/proc/nano_structure()
|
||||
var/cam[0]
|
||||
var/turf/T = get_turf(src)
|
||||
cam["name"] = sanitize(c_tag)
|
||||
cam["deact"] = !can_use()
|
||||
cam["camera"] = "\ref[src]"
|
||||
cam["x"] = x
|
||||
cam["y"] = y
|
||||
cam["z"] = z
|
||||
return cam
|
||||
cam["x"] = T.x
|
||||
cam["y"] = T.y
|
||||
cam["z"] = T.z
|
||||
return cam
|
||||
|
||||
/obj/machinery/camera/portable //Cameras which are placed inside of things, such as helmets.
|
||||
var/turf/prev_turf
|
||||
|
||||
/obj/machinery/camera/portable/New()
|
||||
..()
|
||||
assembly.state = 0 //These cameras are portable, and so shall be in the portable state if removed.
|
||||
assembly.anchored = 0
|
||||
assembly.update_icon()
|
||||
|
||||
/obj/machinery/camera/portable/process() //Updates whenever the camera is moved.
|
||||
if(cameranet && get_turf(src) != prev_turf)
|
||||
cameranet.updatePortableCamera(src)
|
||||
prev_turf = get_turf(src)
|
||||
@@ -1,30 +1,36 @@
|
||||
/mob/living/silicon/ai/var/max_locations = 10
|
||||
/mob/living/silicon/ai/var/stored_locations[0]
|
||||
|
||||
/mob/living/silicon/ai/proc/InvalidTurf(turf/T as turf)
|
||||
if(!T)
|
||||
return 1
|
||||
if((T.z in config.admin_levels))
|
||||
return 1
|
||||
if(T.z > 6)
|
||||
if(T.z > MAX_Z)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/mob/living/silicon/ai/var/max_locations = 10
|
||||
/mob/living/silicon/ai/var/stored_locations[0]
|
||||
|
||||
/mob/living/silicon/ai/proc/get_camera_list()
|
||||
|
||||
track.cameras.Cut()
|
||||
|
||||
if(src.stat == 2)
|
||||
return
|
||||
|
||||
cameranet.process_sort()
|
||||
var/list/L = list()
|
||||
for (var/obj/machinery/camera/C in cameranet.cameras)
|
||||
L.Add(C)
|
||||
|
||||
camera_sort(L)
|
||||
|
||||
var/list/T = list()
|
||||
T["Cancel"] = "Cancel"
|
||||
for (var/obj/machinery/camera/C in cameranet.cameras)
|
||||
var/list/tempnetwork = C.network&src.network
|
||||
|
||||
for (var/obj/machinery/camera/C in L)
|
||||
var/list/tempnetwork = C.network & src.network
|
||||
if (tempnetwork.len)
|
||||
T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C
|
||||
|
||||
track = new()
|
||||
track.cameras = T
|
||||
return T
|
||||
|
||||
@@ -63,7 +69,7 @@
|
||||
src << "\red There is already a stored location by this name"
|
||||
return
|
||||
|
||||
var/L = src.eyeobj.getLoc()
|
||||
var/L = get_turf(eyeobj)
|
||||
if (InvalidTurf(get_turf(L)))
|
||||
src << "\red Unable to store this location"
|
||||
return
|
||||
@@ -108,53 +114,37 @@
|
||||
|
||||
/mob/living/silicon/ai/proc/trackable_mobs()
|
||||
|
||||
track.names.Cut()
|
||||
track.namecounts.Cut()
|
||||
track.humans.Cut()
|
||||
track.others.Cut()
|
||||
|
||||
if(usr.stat == 2)
|
||||
return list()
|
||||
|
||||
var/datum/trackable/TB = new()
|
||||
for(var/mob/living/M in mob_list)
|
||||
// Easy checks first.
|
||||
// Don't detect mobs on Centcom. Since the wizard den is on Centcomm, we only need this.
|
||||
if(InvalidTurf(get_turf(M)))
|
||||
continue
|
||||
if(M == usr)
|
||||
continue
|
||||
if(M.invisibility)//cloaked
|
||||
continue
|
||||
if(M.digitalcamo)
|
||||
if(!M.can_track(usr))
|
||||
continue
|
||||
|
||||
// Human check
|
||||
var/human = 0
|
||||
if(istype(M, /mob/living/carbon/human))
|
||||
human = 1
|
||||
var/mob/living/carbon/human/H = M
|
||||
//Cameras can't track people wearing an untrackable card
|
||||
var/obj/item/weapon/card/id/id = H.wear_id
|
||||
if(istype(id) && id.is_untrackable())
|
||||
continue
|
||||
if(istype(H.head, /obj/item/clothing/head))
|
||||
var/obj/item/clothing/head/hat = H.head
|
||||
if(hat.blockTracking)
|
||||
continue
|
||||
// Now, are they trackable? (This is last because it's the most intensive check)
|
||||
if(!trackable(M))
|
||||
continue
|
||||
|
||||
var/name = M.name
|
||||
if (name in TB.names)
|
||||
TB.namecounts[name]++
|
||||
name = text("[] ([])", name, TB.namecounts[name])
|
||||
if (name in track.names)
|
||||
track.namecounts[name]++
|
||||
name = text("[] ([])", name, track.namecounts[name])
|
||||
else
|
||||
TB.names.Add(name)
|
||||
TB.namecounts[name] = 1
|
||||
track.names.Add(name)
|
||||
track.namecounts[name] = 1
|
||||
if(human)
|
||||
TB.humans[name] = M
|
||||
track.humans[name] = M
|
||||
else
|
||||
TB.others[name] = M
|
||||
track.others[name] = M
|
||||
|
||||
var/list/targets = sortList(track.humans) + sortList(track.others)
|
||||
|
||||
var/list/targets = sortList(TB.humans) + sortList(TB.others)
|
||||
src.track = TB
|
||||
return targets
|
||||
|
||||
/mob/living/silicon/ai/proc/ai_camera_track(target_name in trackable_mobs())
|
||||
@@ -162,14 +152,14 @@
|
||||
set name = "Track With Camera"
|
||||
set desc = "Select who you would like to track."
|
||||
|
||||
if(src.stat == 2)
|
||||
if(src.stat == DEAD)
|
||||
src << "You can't track with camera because you are dead!"
|
||||
return
|
||||
if(!target_name)
|
||||
src.cameraFollow = null
|
||||
return
|
||||
|
||||
var/mob/target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name])
|
||||
src.track = null
|
||||
|
||||
ai_actual_track(target)
|
||||
|
||||
/mob/living/silicon/ai/proc/ai_cancel_tracking(var/forced = 0)
|
||||
@@ -177,65 +167,74 @@
|
||||
return
|
||||
|
||||
src << "Follow camera mode [forced ? "terminated" : "ended"]."
|
||||
cameraFollow.tracking_cancelled()
|
||||
cameraFollow = null
|
||||
|
||||
/mob/living/silicon/ai/proc/ai_actual_track(atom/movable/target as mob|obj)
|
||||
if(!istype(target)) return
|
||||
/mob/living/silicon/ai/proc/ai_actual_track(mob/living/target)
|
||||
if(!istype(target))
|
||||
return
|
||||
var/mob/living/silicon/ai/U = usr
|
||||
|
||||
U.cameraFollow = target
|
||||
U << "Now tracking [target.name] on camera."
|
||||
target.tracking_initiated()
|
||||
U.tracking = 1
|
||||
|
||||
spawn (0)
|
||||
while (U.cameraFollow == target)
|
||||
if (U.cameraFollow == null)
|
||||
U << "<span class='notice'>Attempting to track [target.get_visible_name()]...</span>"
|
||||
sleep(min(30, get_dist(target, U.eyeobj) / 4))
|
||||
spawn(15) //give the AI a grace period to stop moving.
|
||||
U.tracking = 0
|
||||
|
||||
if(!target || !target.can_track(usr))
|
||||
U << "<span class='warning'>Target is not near any active cameras.</span>"
|
||||
U.cameraFollow = null
|
||||
return
|
||||
|
||||
U << "<span class='notice'>Now tracking [target.get_visible_name()] on camera.</span>"
|
||||
|
||||
var/cameraticks = 0
|
||||
spawn(0)
|
||||
while(U.cameraFollow == target)
|
||||
if(U.cameraFollow == null)
|
||||
return
|
||||
|
||||
if (!trackable(target))
|
||||
U << "<span class='warning'>Target is not on or near any active cameras on the station.</span>"
|
||||
sleep(100)
|
||||
continue
|
||||
if(!target.can_track(usr))
|
||||
U.tracking = 1
|
||||
if(!cameraticks)
|
||||
U << "<span class='warning'>Target is not near any active cameras. Attempting to reacquire...</span>"
|
||||
cameraticks++
|
||||
if(cameraticks > 9)
|
||||
U.cameraFollow = null
|
||||
U << "<span class='warning'>Unable to reacquire, cancelling track...</span>"
|
||||
U.tracking = 0
|
||||
return
|
||||
else
|
||||
sleep(10)
|
||||
continue
|
||||
|
||||
else
|
||||
cameraticks = 0
|
||||
U.tracking = 0
|
||||
|
||||
if(U.eyeobj)
|
||||
U.eyeobj.setLoc(get_turf(target), 0)
|
||||
U.eyeobj.setLoc(get_turf(target))
|
||||
|
||||
else
|
||||
view_core()
|
||||
U.cameraFollow = null
|
||||
return
|
||||
|
||||
sleep(10)
|
||||
|
||||
/proc/near_camera(var/mob/living/M)
|
||||
/proc/near_camera(mob/living/M)
|
||||
if (!isturf(M.loc))
|
||||
return 0
|
||||
if(isrobot(M))
|
||||
var/mob/living/silicon/robot/R = M
|
||||
if(!(R.camera && R.camera.can_use()) && !cameranet.checkCameraVis(M))
|
||||
return 0
|
||||
else if(!isturf(M.loc) || !cameranet.checkCameraVis(M))
|
||||
else if(!cameranet.checkCameraVis(M))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/proc/trackable(atom/movable/M)
|
||||
var/turf/T = get_turf(M)
|
||||
if (istype(M, /mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/H = M
|
||||
var/obj/item/weapon/card/id/id = H.wear_id
|
||||
if(istype(id) && id.is_untrackable())
|
||||
return 0
|
||||
if(H.digitalcamo)
|
||||
return 0
|
||||
if(istype(H.head, /obj/item/clothing/head))
|
||||
var/obj/item/clothing/head/hat = H.head
|
||||
if (hat.blockTracking)
|
||||
return 0
|
||||
if(istype(M.loc,/obj/effect/dummy))
|
||||
return 0
|
||||
|
||||
if((T && (T.z in config.contact_levels)) && ((hassensorlevel(M, SUIT_SENSOR_TRACKING) || M in aibots)))
|
||||
return 1
|
||||
|
||||
return near_camera(M)
|
||||
|
||||
/obj/machinery/camera/attack_ai(var/mob/living/silicon/ai/user as mob)
|
||||
/obj/machinery/camera/attack_ai(mob/living/silicon/ai/user)
|
||||
if (!istype(user))
|
||||
return
|
||||
if (!src.can_use())
|
||||
@@ -243,19 +242,21 @@
|
||||
user.eyeobj.setLoc(get_turf(src))
|
||||
|
||||
|
||||
/mob/living/silicon/ai/attack_ai(var/mob/user as mob)
|
||||
/mob/living/silicon/ai/attack_ai(mob/user)
|
||||
ai_camera_list()
|
||||
|
||||
/atom/movable/proc/tracking_initiated()
|
||||
/proc/camera_sort(list/L)
|
||||
var/obj/machinery/camera/a
|
||||
var/obj/machinery/camera/b
|
||||
|
||||
/mob/living/silicon/robot/tracking_initiated()
|
||||
tracking_entities++
|
||||
if(tracking_entities == 1 && has_zeroth_law())
|
||||
src << "<span class='warning'>Internal camera is currently being accessed.</span>"
|
||||
|
||||
/atom/movable/proc/tracking_cancelled()
|
||||
|
||||
/mob/living/silicon/robot/tracking_initiated()
|
||||
tracking_entities--
|
||||
if(!tracking_entities && has_zeroth_law())
|
||||
src << "<span class='notice'>Internal camera is no longer being accessed.</span>"
|
||||
for (var/i = L.len, i > 0, i--)
|
||||
for (var/j = 1 to i - 1)
|
||||
a = L[j]
|
||||
b = L[j + 1]
|
||||
if (a.c_tag_order != b.c_tag_order)
|
||||
if (a.c_tag_order > b.c_tag_order)
|
||||
L.Swap(j, j + 1)
|
||||
else
|
||||
if (sorttext(a.c_tag, b.c_tag) < 0)
|
||||
L.Swap(j, j + 1)
|
||||
return L
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
/var/camera_cache_id = 1
|
||||
|
||||
/proc/invalidateCameraCache()
|
||||
camera_cache_id = (++camera_cache_id % 999999)
|
||||
|
||||
/obj/machinery/computer/security
|
||||
name = "Camera Monitor"
|
||||
desc = "Used to access the various cameras networks on the station."
|
||||
@@ -11,38 +6,35 @@
|
||||
circuit = /obj/item/weapon/circuitboard/camera
|
||||
var/obj/machinery/camera/current = null
|
||||
var/list/network = list("")
|
||||
var/last_pic = 1.0
|
||||
var/last_pic = 1
|
||||
light_color = LIGHT_COLOR_RED
|
||||
var/mapping = 0
|
||||
var/cache_id = 0
|
||||
var/list/networks[0]
|
||||
var/list/tempnets[0]
|
||||
var/list/data[0]
|
||||
var/list/access[0]
|
||||
var/camera_cache = null
|
||||
|
||||
/obj/machinery/computer/security/New() // Lists existing networks and their required access. Format: networks[<name>] = list(<access>)
|
||||
networks["SS13"] = list(access_hos,access_captain)
|
||||
networks["Telecomms"] = list(access_hos,access_captain)
|
||||
networks["Research Outpost"] = list(access_rd,access_hos,access_captain)
|
||||
networks["Mining Outpost"] = list(access_qm,access_hop,access_hos,access_captain)
|
||||
networks["Research"] = list(access_rd,access_hos,access_captain)
|
||||
networks["Prison"] = list(access_hos,access_captain)
|
||||
networks["Labor"] = list(access_hos,access_captain)
|
||||
networks["Interrogation"] = list(access_hos,access_captain)
|
||||
networks["SS13"] = list(access_hos,access_captain)
|
||||
networks["Telecomms"] = list(access_hos,access_captain)
|
||||
networks["Research Outpost"] = list(access_rd,access_hos,access_captain)
|
||||
networks["Mining Outpost"] = list(access_qm,access_hop,access_hos,access_captain)
|
||||
networks["Research"] = list(access_rd,access_hos,access_captain)
|
||||
networks["Prison"] = list(access_hos,access_captain)
|
||||
networks["Labor"] = list(access_hos,access_captain)
|
||||
networks["Interrogation"] = list(access_hos,access_captain)
|
||||
networks["Atmosphere Alarms"] = list(access_ce,access_hos,access_captain)
|
||||
networks["Fire Alarms"] = list(access_ce,access_hos,access_captain)
|
||||
networks["Power Alarms"] = list(access_ce,access_hos,access_captain)
|
||||
networks["Supermatter"] = list(access_ce,access_hos,access_captain)
|
||||
networks["MiniSat"] = list(access_rd,access_hos,access_captain)
|
||||
networks["Singularity"] = list(access_ce,access_hos,access_captain)
|
||||
networks["Fire Alarms"] = list(access_ce,access_hos,access_captain)
|
||||
networks["Power Alarms"] = list(access_ce,access_hos,access_captain)
|
||||
networks["Supermatter"] = list(access_ce,access_hos,access_captain)
|
||||
networks["MiniSat"] = list(access_rd,access_hos,access_captain)
|
||||
networks["Singularity"] = list(access_ce,access_hos,access_captain)
|
||||
networks["Anomaly Isolation"] = list(access_rd,access_hos,access_captain)
|
||||
networks["Toxins"] = list(access_rd,access_hos,access_captain)
|
||||
networks["Telepad"] = list(access_rd,access_hos,access_captain)
|
||||
networks["TestChamber"] = list(access_rd,access_hos,access_captain)
|
||||
networks["ERT"] = list(access_cent_specops_commander,access_cent_commander)
|
||||
networks["CentComm"] = list(access_cent_security,access_cent_commander)
|
||||
networks["Thunderdome"] = list(access_cent_thunder,access_cent_commander)
|
||||
networks["Toxins"] = list(access_rd,access_hos,access_captain)
|
||||
networks["Telepad"] = list(access_rd,access_hos,access_captain)
|
||||
networks["TestChamber"] = list(access_rd,access_hos,access_captain)
|
||||
networks["ERT"] = list(access_cent_specops_commander,access_cent_commander)
|
||||
networks["CentComm"] = list(access_cent_security,access_cent_commander)
|
||||
networks["Thunderdome"] = list(access_cent_thunder,access_cent_commander)
|
||||
|
||||
..()
|
||||
|
||||
@@ -75,50 +67,55 @@
|
||||
ui_interact(user)
|
||||
|
||||
/obj/machinery/computer/security/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
if(src.z > 6) return
|
||||
if(stat & (NOPOWER|BROKEN)) return
|
||||
if(user.stat) return
|
||||
if(stat & (NOPOWER|BROKEN))
|
||||
return
|
||||
if(user.stat)
|
||||
return
|
||||
|
||||
var/data[0]
|
||||
|
||||
data["current"] = null
|
||||
|
||||
if(camera_cache_id != cache_id)
|
||||
cache_id = camera_cache_id
|
||||
cameranet.process_sort()
|
||||
var/list/cameras = list()
|
||||
for(var/obj/machinery/camera/C in cameranet.cameras)
|
||||
if((z > MAX_Z || C.z > MAX_Z) && (C.z != z)) //can only recieve away mission cameras on away missions
|
||||
continue
|
||||
if(!can_access_camera(C))
|
||||
continue
|
||||
|
||||
var/cameras[0]
|
||||
for(var/obj/machinery/camera/C in cameranet.cameras)
|
||||
if(!can_access_camera(C))
|
||||
continue
|
||||
cameras[++cameras.len] = C.nano_structure()
|
||||
|
||||
var/cam = C.nano_structure()
|
||||
cameras[++cameras.len] = cam
|
||||
for(var/i = cameras.len, i > 0, i--) //based off /proc/camera_sort, sorts cameras alphabetically for the UI
|
||||
for(var/j = 1 to i - 1)
|
||||
var/a = cameras[j]
|
||||
var/b = cameras[j + 1]
|
||||
if(sorttext(a["name"], b["name"]) < 0)
|
||||
cameras.Swap(j, j + 1)
|
||||
|
||||
camera_cache=list2json(cameras)
|
||||
data["cameras"] = cameras
|
||||
|
||||
tempnets.Cut()
|
||||
if(emagged)
|
||||
access = list(access_captain) // Assume captain level access when emagged
|
||||
data["emagged"] = 1
|
||||
|
||||
if(isAI(user) || isrobot(user))
|
||||
access = list(access_captain) // Assume captain level access when AI
|
||||
|
||||
var/tempnets[0]
|
||||
// Loop through the ID's permission, and check which networks the ID has access to.
|
||||
for(var/l in networks) // Loop through networks.
|
||||
for(var/m in networks[l]) // Loop through access levels of the networks.
|
||||
if(m in access)
|
||||
if(l in network) // Checks if the network is currently active.
|
||||
tempnets.Add(list(list("name" = l, "active" = 1)))
|
||||
for(var/net in networks) // Loop through networks.
|
||||
for(var/req in networks[net]) // Loop through access levels of the networks.
|
||||
if(req in access)
|
||||
if(net in network) // Checks if the network is currently active.
|
||||
tempnets.Add(list(list("name" = net, "active" = 1)))
|
||||
else
|
||||
tempnets.Add(list(list("name" = l, "active" = 0)))
|
||||
tempnets.Add(list(list("name" = net, "active" = 0)))
|
||||
break
|
||||
|
||||
if(tempnets.len)
|
||||
data["networks"] = tempnets
|
||||
|
||||
if(current)
|
||||
data["current"] = current.nano_structure()
|
||||
data["cameras"] = list("__json_cache" = camera_cache)
|
||||
|
||||
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
@@ -134,20 +131,24 @@
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/obj/machinery/computer/security/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
if(href_list["switchTo"])
|
||||
if(src.z>6 || stat&(NOPOWER|BROKEN)) return
|
||||
if(usr.stat || ((get_dist(usr, src) > 1 || !( usr.canmove ) || usr.blinded) && !istype(usr, /mob/living/silicon))) return
|
||||
if(stat & (NOPOWER|BROKEN))
|
||||
return 1
|
||||
var/obj/machinery/camera/C = locate(href_list["switchTo"]) in cameranet.cameras
|
||||
if(!C) return
|
||||
if(!C)
|
||||
return 1
|
||||
|
||||
switch_to_camera(usr, C)
|
||||
return 1
|
||||
|
||||
else if(href_list["reset"])
|
||||
if(src.z>6 || stat&(NOPOWER|BROKEN)) return
|
||||
if(usr.stat || ((get_dist(usr, src) > 1 || !( usr.canmove ) || usr.blinded) && !istype(usr, /mob/living/silicon))) return
|
||||
if(stat & (NOPOWER|BROKEN))
|
||||
return 1
|
||||
reset_current()
|
||||
usr.check_eye(current)
|
||||
return 1
|
||||
|
||||
else if(href_list["activate"]) // Activate: enable or disable networks
|
||||
var/net = href_list["activate"] // Network to be enabled or disabled.
|
||||
var/active = href_list["active"] // Is the network currently active.
|
||||
@@ -159,17 +160,13 @@
|
||||
else
|
||||
src.network += net
|
||||
break
|
||||
invalidateCameraCache()
|
||||
nanomanager.update_uis(src)
|
||||
else
|
||||
. = ..()
|
||||
|
||||
/obj/machinery/computer/security/attack_hand(var/mob/user as mob)
|
||||
|
||||
/obj/machinery/computer/security/attack_hand(mob/user)
|
||||
access = list()
|
||||
if (src.z > 6)
|
||||
user << "\red <b>Unable to establish a connection</b>: \black You're too far away from the station!"
|
||||
if(stat & (NOPOWER|BROKEN))
|
||||
return
|
||||
if(stat & (NOPOWER|BROKEN)) return
|
||||
|
||||
if(!isAI(user))
|
||||
user.set_machine(src)
|
||||
@@ -191,9 +188,9 @@
|
||||
|
||||
// Switching to cameras
|
||||
/obj/machinery/computer/security/proc/switch_to_camera(var/mob/user, var/obj/machinery/camera/C)
|
||||
if ((get_dist(user, src) > 1 || user.machine != src || user.blinded || !( user.canmove ) || !( C.can_use() )) && (!istype(user, /mob/living/silicon/ai)))
|
||||
if((get_dist(user, src) > 1 || user.machine != src || user.blinded || !(user.canmove) || !C.can_use()) && !(istype(user, /mob/living/silicon/ai)))
|
||||
if(!C.can_use() && !isAI(user))
|
||||
src.current = null
|
||||
current = null
|
||||
return 0
|
||||
else
|
||||
if(isAI(user))
|
||||
@@ -215,18 +212,11 @@
|
||||
if(current)
|
||||
reset_current()
|
||||
|
||||
src.current = C
|
||||
current = C
|
||||
if(current)
|
||||
use_power = 2
|
||||
var/mob/living/L = current.loc
|
||||
if(istype(L))
|
||||
L.tracking_initiated()
|
||||
|
||||
/obj/machinery/computer/security/proc/reset_current()
|
||||
if(current)
|
||||
var/mob/living/L = current.loc
|
||||
if(istype(L))
|
||||
L.tracking_cancelled()
|
||||
current = null
|
||||
use_power = 1
|
||||
|
||||
|
||||
@@ -65,7 +65,6 @@
|
||||
for(var/obj/machinery/camera/C in alarm.cameras())
|
||||
if(was_raised)
|
||||
C.network.Add(category)
|
||||
invalidateCameraCache()
|
||||
else
|
||||
C.network.Remove(category)
|
||||
notify_listeners(alarm, was_raised)
|
||||
|
||||
@@ -190,7 +190,6 @@
|
||||
if(temp.len)
|
||||
L.Add(C)
|
||||
|
||||
cameranet.process_sort()
|
||||
|
||||
return L
|
||||
verify_machine(var/obj/machinery/camera/C,var/datum/file/camnet_key/key = null)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
mouse_opacity = 0
|
||||
see_in_dark = 7
|
||||
invisibility = 101 // No one can see us
|
||||
|
||||
sight = SEE_SELF
|
||||
move_on_shuttle = 0
|
||||
|
||||
/mob/camera/experience_pressure_difference()
|
||||
@@ -17,7 +17,7 @@
|
||||
/mob/camera/Destroy()
|
||||
..()
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
|
||||
|
||||
/mob/camera/Login()
|
||||
..()
|
||||
update_interface()
|
||||
|
||||
@@ -694,10 +694,10 @@
|
||||
return
|
||||
|
||||
//repurposed proc. Now it combines get_id_name() and get_face_name() to determine a mob's name variable. Made into a seperate proc as it'll be useful elsewhere
|
||||
/mob/living/carbon/human/proc/get_visible_name()
|
||||
if( wear_mask && (wear_mask.flags_inv&HIDEFACE) ) //Wearing a mask which hides our face, use id-name if possible
|
||||
/mob/living/carbon/human/get_visible_name()
|
||||
if(wear_mask && (wear_mask.flags_inv & HIDEFACE)) //Wearing a mask which hides our face, use id-name if possible
|
||||
return get_id_name("Unknown")
|
||||
if( head && (head.flags_inv&HIDEFACE) )
|
||||
if(head && (head.flags_inv & HIDEFACE))
|
||||
return get_id_name("Unknown") //Likewise for hats
|
||||
var/face_name = get_face_name()
|
||||
var/id_name = get_id_name("")
|
||||
@@ -1911,3 +1911,15 @@
|
||||
else
|
||||
src << "<span class='notice'>You swallow a gulp of [toDrink].</span>"
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/human/can_track(mob/living/user)
|
||||
if(wear_id)
|
||||
var/obj/item/weapon/card/id/id = wear_id
|
||||
if(istype(id) && id.is_untrackable())
|
||||
return 0
|
||||
if(istype(head, /obj/item/clothing/head))
|
||||
var/obj/item/clothing/head/hat = head
|
||||
if(hat.blockTracking)
|
||||
return 0
|
||||
|
||||
return ..()
|
||||
@@ -10,6 +10,28 @@
|
||||
if(rig)
|
||||
SetupStat(rig)
|
||||
|
||||
/mob/living/proc/can_track(mob/living/user)
|
||||
//basic fast checks go first. When overriding this proc, I recommend calling ..() at the end.
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
return 0
|
||||
if(T.z == ZLEVEL_CENTCOMM) //dont detect mobs on centcomm
|
||||
return 0
|
||||
if(T.z >= MAX_Z)
|
||||
return 0
|
||||
if(user != null && src == user)
|
||||
return 0
|
||||
if(invisibility || alpha == 0)//cloaked
|
||||
return 0
|
||||
if(digitalcamo)
|
||||
return 0
|
||||
|
||||
// Now, are they viewable by a camera? (This is last because it's the most intensive check)
|
||||
if(!near_camera(src))
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
//mob verbs are a lot faster than object verbs
|
||||
//for more info on why this is not atom/pull, see examinate() in mob.dm
|
||||
/mob/living/verb/pulled(atom/movable/AM as mob|obj in oview(1))
|
||||
@@ -760,6 +782,9 @@
|
||||
src << "<span class='notice'>You're too exhausted to keep going...</span>"
|
||||
Weaken(5)
|
||||
|
||||
/mob/living/proc/get_visible_name()
|
||||
return name
|
||||
|
||||
/mob/living/update_gravity(has_gravity)
|
||||
if(!ticker)
|
||||
return
|
||||
|
||||
@@ -46,7 +46,7 @@ var/list/ai_verbs_default = list(
|
||||
var/list/connected_robots = list()
|
||||
var/aiRestorePowerRoutine = 0
|
||||
//var/list/laws = list()
|
||||
var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list())
|
||||
var/alarms = list("Motion" = list(), "Fire" = list(), "Atmosphere" = list(), "Power" = list(), "Camera" = list())
|
||||
var/viewalerts = 0
|
||||
var/icon/holo_icon//Default is assigned when AI is created.
|
||||
var/obj/mecha/controlled_mech //For controlled_mech a mech, to determine whether to relaymove or use the AI eye.
|
||||
@@ -69,10 +69,13 @@ var/list/ai_verbs_default = list(
|
||||
var/obj/machinery/power/apc/malfhack = null
|
||||
var/explosive = 0 //does the AI explode when it dies?
|
||||
|
||||
var/mob/living/silicon/ai/parent = null
|
||||
|
||||
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/mob/living/silicon/ai/parent = null
|
||||
var/camera_light_on = 0
|
||||
var/list/obj/machinery/camera/lit_cameras = list()
|
||||
|
||||
var/datum/trackable/track = new()
|
||||
|
||||
var/last_paper_seen = null
|
||||
var/can_shunt = 1
|
||||
var/last_announcement = ""
|
||||
@@ -81,6 +84,15 @@ var/list/ai_verbs_default = list(
|
||||
var/turf/waypoint //Holds the turf of the currently selected waypoint.
|
||||
var/waypoint_mode = 0 //Waypoint mode is for selecting a turf via clicking.
|
||||
|
||||
var/obj/machinery/hologram/holopad/holo = null
|
||||
var/mob/camera/aiEye/eyeobj = new()
|
||||
var/sprint = 10
|
||||
var/cooldown = 0
|
||||
var/acceleration = 1
|
||||
var/tracking = 0 //this is 1 if the AI is currently tracking somebody, but the track has not yet been completed.
|
||||
|
||||
var/obj/machinery/camera/portable/builtInCamera
|
||||
|
||||
//var/obj/item/borg/sight/hud/sec/sechud = null
|
||||
//var/obj/item/borg/sight/hud/med/healthhud = null
|
||||
|
||||
@@ -175,6 +187,13 @@ var/list/ai_verbs_default = list(
|
||||
spawn(5)
|
||||
new /obj/machinery/ai_powersupply(src)
|
||||
|
||||
eyeobj.ai = src
|
||||
eyeobj.name = "[src.name] (AI Eye)" // Give it a name
|
||||
eyeobj.loc = src.loc
|
||||
|
||||
builtInCamera = new /obj/machinery/camera/portable(src)
|
||||
builtInCamera.c_tag = name
|
||||
builtInCamera.network = list("SS13")
|
||||
|
||||
ai_list += src
|
||||
shuttle_caller_list += src
|
||||
@@ -486,8 +505,8 @@ var/list/ai_verbs_default = list(
|
||||
return
|
||||
|
||||
if (href_list["track"])
|
||||
var/mob/target = locate(href_list["track"]) in mob_list
|
||||
if(target && trackable(target))
|
||||
var/mob/living/target = locate(href_list["track"]) in mob_list
|
||||
if(target && target.can_track())
|
||||
ai_actual_track(target)
|
||||
else
|
||||
src << "<span class='warning'>Target is not on or near any active cameras on the station.</span>"
|
||||
@@ -495,7 +514,7 @@ var/list/ai_verbs_default = list(
|
||||
|
||||
if (href_list["trackbot"])
|
||||
var/obj/machinery/bot/target = locate(href_list["trackbot"]) in aibots
|
||||
if(target && trackable(target))
|
||||
if(target)
|
||||
ai_actual_track(target)
|
||||
else
|
||||
src << "<span class='warning'>Target is not on or near any active cameras on the station.</span>"
|
||||
@@ -633,7 +652,6 @@ var/list/ai_verbs_default = list(
|
||||
d += "<td width='30%'>[bot_area.name]</td>"
|
||||
d += "<td width='10%'><A HREF=?src=\ref[src];interface=\ref[Bot]>Interface</A></td>"
|
||||
d += "<td width='10%'><A HREF=?src=\ref[src];callbot=\ref[Bot]>Call</A></td>"
|
||||
d += "<td width='10%'><a href='byond://?src=\ref[src];track2=\ref[src];trackbot=\ref[Bot]'>Track</A></td>"
|
||||
d += "</tr>"
|
||||
d = format_text(d)
|
||||
|
||||
@@ -664,7 +682,10 @@ var/list/ai_verbs_default = list(
|
||||
|
||||
/mob/living/silicon/ai/proc/switchCamera(var/obj/machinery/camera/C)
|
||||
|
||||
if (!C || stat == 2) //C.can_use())
|
||||
if(!tracking)
|
||||
cameraFollow = null
|
||||
|
||||
if (!C || stat == DEAD) //C.can_use())
|
||||
return 0
|
||||
|
||||
if(!src.eyeobj)
|
||||
@@ -825,17 +846,20 @@ var/list/ai_verbs_default = list(
|
||||
if(stat != CONSCIOUS)
|
||||
return
|
||||
|
||||
if(check_unable())
|
||||
camera_light_on = !camera_light_on
|
||||
|
||||
if (!camera_light_on)
|
||||
src << "Camera lights deactivated."
|
||||
|
||||
for (var/obj/machinery/camera/C in lit_cameras)
|
||||
C.set_light(0)
|
||||
lit_cameras = list()
|
||||
|
||||
return
|
||||
|
||||
camera_light_on = !camera_light_on
|
||||
src << "Camera lights [camera_light_on ? "activated" : "deactivated"]."
|
||||
if(!camera_light_on)
|
||||
if(current)
|
||||
current.set_light(0)
|
||||
current = null
|
||||
else
|
||||
lightNearbyCamera()
|
||||
light_cameras()
|
||||
|
||||
src << "Camera lights activated."
|
||||
|
||||
/mob/living/silicon/ai/proc/sensor_mode()
|
||||
set name = "Set Sensor Augmentation"
|
||||
@@ -856,26 +880,25 @@ var/list/ai_verbs_default = list(
|
||||
// Handled camera lighting, when toggled.
|
||||
// It will get the nearest camera from the eyeobj, lighting it.
|
||||
|
||||
/mob/living/silicon/ai/proc/lightNearbyCamera()
|
||||
if(camera_light_on && camera_light_on < world.timeofday)
|
||||
if(src.current)
|
||||
var/obj/machinery/camera/camera = near_range_camera(src.eyeobj)
|
||||
if(camera && src.current != camera)
|
||||
src.current.set_light(0)
|
||||
if(!camera.light_disabled)
|
||||
src.current = camera
|
||||
src.current.set_light(AI_CAMERA_LUMINOSITY)
|
||||
else
|
||||
src.current = null
|
||||
else if(isnull(camera))
|
||||
src.current.set_light(0)
|
||||
src.current = null
|
||||
else
|
||||
var/obj/machinery/camera/camera = near_range_camera(src.eyeobj)
|
||||
if(camera && !camera.light_disabled)
|
||||
src.current = camera
|
||||
src.current.set_light(AI_CAMERA_LUMINOSITY)
|
||||
camera_light_on = world.timeofday + 1 * 20 // Update the light every 2 seconds.
|
||||
/mob/living/silicon/ai/proc/light_cameras()
|
||||
var/list/obj/machinery/camera/add = list()
|
||||
var/list/obj/machinery/camera/remove = list()
|
||||
var/list/obj/machinery/camera/visible = list()
|
||||
for (var/datum/camerachunk/CC in eyeobj.visibleCameraChunks)
|
||||
for (var/obj/machinery/camera/C in CC.cameras)
|
||||
if (!C.can_use() || get_dist(C, eyeobj) > 7)
|
||||
continue
|
||||
visible |= C
|
||||
|
||||
add = visible - lit_cameras
|
||||
remove = lit_cameras - visible
|
||||
|
||||
for (var/obj/machinery/camera/C in remove)
|
||||
lit_cameras -= C //Removed from list before turning off the light so that it doesn't check the AI looking away.
|
||||
C.Togglelight(0)
|
||||
for (var/obj/machinery/camera/C in add)
|
||||
C.Togglelight(1)
|
||||
lit_cameras |= C
|
||||
|
||||
|
||||
/mob/living/silicon/ai/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
|
||||
|
||||
@@ -2,33 +2,34 @@
|
||||
//
|
||||
// The datum containing all the chunks.
|
||||
|
||||
var/const/CHUNK_SIZE = 16 // Only chunk sizes that are to the power of 2. E.g: 2, 4, 8, 16, etc..
|
||||
|
||||
var/datum/cameranet/cameranet = new()
|
||||
|
||||
/datum/cameranet
|
||||
var/name = "Camera Net" // Name to show for VV and stat()
|
||||
|
||||
// The cameras on the map, no matter if they work or not. Updated in obj/machinery/camera.dm by New() and Destroy().
|
||||
var/list/cameras = list()
|
||||
var/cameras_unsorted = 1
|
||||
// The chunks of the map, mapping the areas that the cameras can see.
|
||||
var/list/chunks = list()
|
||||
var/ready = 0
|
||||
|
||||
/datum/cameranet/proc/process_sort()
|
||||
if(cameras_unsorted)
|
||||
cameras = dd_sortedObjectList(cameras)
|
||||
cameras_unsorted = 0
|
||||
// The object used for the clickable stat() button.
|
||||
var/obj/effect/statclick/statclick
|
||||
|
||||
// Checks if a chunk has been Generated in x, y, z.
|
||||
/datum/cameranet/proc/chunkGenerated(x, y, z)
|
||||
x &= ~0xf
|
||||
y &= ~0xf
|
||||
x &= ~(CHUNK_SIZE - 1)
|
||||
y &= ~(CHUNK_SIZE - 1)
|
||||
var/key = "[x],[y],[z]"
|
||||
return (chunks[key])
|
||||
|
||||
// Returns the chunk in the x, y, z.
|
||||
// If there is no chunk, it creates a new chunk and returns that.
|
||||
/datum/cameranet/proc/getCameraChunk(x, y, z)
|
||||
x &= ~0xf
|
||||
y &= ~0xf
|
||||
x &= ~(CHUNK_SIZE - 1)
|
||||
y &= ~(CHUNK_SIZE - 1)
|
||||
var/key = "[x],[y],[z]"
|
||||
if(!chunks[key])
|
||||
chunks[key] = new /datum/camerachunk(null, x, y, z)
|
||||
@@ -37,12 +38,12 @@ var/datum/cameranet/cameranet = new()
|
||||
|
||||
// Updates what the aiEye can see. It is recommended you use this when the aiEye moves or it's location is set.
|
||||
|
||||
/datum/cameranet/proc/visibility(mob/aiEye/ai)
|
||||
/datum/cameranet/proc/visibility(mob/camera/aiEye/ai)
|
||||
// 0xf = 15
|
||||
var/x1 = max(0, ai.x - 16) & ~0xf
|
||||
var/y1 = max(0, ai.y - 16) & ~0xf
|
||||
var/x2 = min(world.maxx, ai.x + 16) & ~0xf
|
||||
var/y2 = min(world.maxy, ai.y + 16) & ~0xf
|
||||
var/x1 = max(0, ai.x - 16) & ~(CHUNK_SIZE - 1)
|
||||
var/y1 = max(0, ai.y - 16) & ~(CHUNK_SIZE - 1)
|
||||
var/x2 = min(world.maxx, ai.x + 16) & ~(CHUNK_SIZE - 1)
|
||||
var/y2 = min(world.maxy, ai.y + 16) & ~(CHUNK_SIZE - 1)
|
||||
|
||||
var/list/visibleChunks = list()
|
||||
|
||||
@@ -63,7 +64,7 @@ var/datum/cameranet/cameranet = new()
|
||||
|
||||
// Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open.
|
||||
|
||||
/datum/cameranet/proc/updateVisibility(atom/A, var/opacity_check = 1)
|
||||
/datum/cameranet/proc/updateVisibility(atom/A, opacity_check = 1)
|
||||
|
||||
if(!ticker || (opacity_check && !A.opacity))
|
||||
return
|
||||
@@ -109,15 +110,15 @@ var/datum/cameranet/cameranet = new()
|
||||
|
||||
var/turf/T = get_turf(c)
|
||||
if(T)
|
||||
var/x1 = max(0, T.x - 8) & ~0xf
|
||||
var/y1 = max(0, T.y - 8) & ~0xf
|
||||
var/x2 = min(world.maxx, T.x + 8) & ~0xf
|
||||
var/y2 = min(world.maxy, T.y + 8) & ~0xf
|
||||
var/x1 = max(0, T.x - (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
var/y1 = max(0, T.y - (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
var/x2 = min(world.maxx, T.x + (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
var/y2 = min(world.maxy, T.y + (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
|
||||
//world << "X1: [x1] - Y1: [y1] - X2: [x2] - Y2: [y2]"
|
||||
|
||||
for(var/x = x1; x <= x2; x += 16)
|
||||
for(var/y = y1; y <= y2; y += 16)
|
||||
for(var/x = x1; x <= x2; x += CHUNK_SIZE)
|
||||
for(var/y = y1; y <= y2; y += CHUNK_SIZE)
|
||||
if(chunkGenerated(x, y, T.z))
|
||||
var/datum/camerachunk/chunk = getCameraChunk(x, y, T.z)
|
||||
if(choice == 0)
|
||||
@@ -145,6 +146,14 @@ var/datum/cameranet/cameranet = new()
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/*
|
||||
/datum/cameranet/proc/stat_entry()
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug("Initializing...", src)
|
||||
|
||||
stat(name, statclick.update("Cameras: [cameranet.cameras.len] | Chunks: [cameranet.chunks.len]"))
|
||||
*/
|
||||
|
||||
// Debug verb for VVing the chunk that the turf is in.
|
||||
/*
|
||||
/turf/verb/view_chunk()
|
||||
|
||||
@@ -21,26 +21,24 @@
|
||||
|
||||
// Add an AI eye to the chunk, then update if changed.
|
||||
|
||||
/datum/camerachunk/proc/add(mob/aiEye/ai)
|
||||
if(!ai.ai)
|
||||
return
|
||||
ai.visibleCameraChunks += src
|
||||
if(ai.ai.client)
|
||||
ai.ai.client.images += obscured
|
||||
/datum/camerachunk/proc/add(mob/camera/aiEye/eye)
|
||||
var/client/client = eye.GetViewerClient()
|
||||
if(client)
|
||||
client.images += obscured
|
||||
eye.visibleCameraChunks += src
|
||||
visible++
|
||||
seenby += ai
|
||||
seenby += eye
|
||||
if(changed && !updating)
|
||||
update()
|
||||
|
||||
// Remove an AI eye from the chunk, then update if changed.
|
||||
|
||||
/datum/camerachunk/proc/remove(mob/aiEye/ai)
|
||||
if(!ai.ai)
|
||||
return
|
||||
ai.visibleCameraChunks -= src
|
||||
if(ai.ai.client)
|
||||
ai.ai.client.images -= obscured
|
||||
seenby -= ai
|
||||
/datum/camerachunk/proc/remove(mob/camera/aiEye/eye)
|
||||
var/client/client = eye.GetViewerClient()
|
||||
if(client)
|
||||
client.images -= obscured
|
||||
eye.visibleCameraChunks -= src
|
||||
seenby -= eye
|
||||
if(visible > 0)
|
||||
visible--
|
||||
|
||||
@@ -54,7 +52,7 @@
|
||||
// Updates the chunk, makes sure that it doesn't update too much. If the chunk isn't being watched it will
|
||||
// instead be flagged to update the next time an AI Eye moves near it.
|
||||
|
||||
/datum/camerachunk/proc/hasChanged(var/update_now = 0)
|
||||
/datum/camerachunk/proc/hasChanged(update_now = 0)
|
||||
if(visible || update_now)
|
||||
if(!updating)
|
||||
updating = 1
|
||||
@@ -68,7 +66,7 @@
|
||||
|
||||
/datum/camerachunk/proc/update()
|
||||
|
||||
set background = 1
|
||||
set background = BACKGROUND_ENABLED
|
||||
|
||||
var/list/newVisibleTurfs = list()
|
||||
|
||||
@@ -81,11 +79,14 @@
|
||||
if(!c.can_use())
|
||||
continue
|
||||
|
||||
var/turf/point = locate(src.x + 8, src.y + 8, src.z)
|
||||
if(get_dist(point, c) > 24)
|
||||
var/turf/point = locate(src.x + (CHUNK_SIZE / 2), src.y + (CHUNK_SIZE / 2), src.z)
|
||||
if(get_dist(point, c) > CHUNK_SIZE + (CHUNK_SIZE / 2))
|
||||
continue
|
||||
|
||||
for(var/turf/t in c.can_see())
|
||||
// Possible optimization: if(turfs[t]) here, rather than &= turfs afterwards.
|
||||
// List associations use a tree or hashmap of some sort (alongside the list itself)
|
||||
// so are surprisingly fast. (significantly faster than var/thingy/x in list, in testing)
|
||||
newVisibleTurfs[t] = t
|
||||
|
||||
// Removes turf that isn't in turfs.
|
||||
@@ -102,11 +103,12 @@
|
||||
if(t.obscured)
|
||||
obscured -= t.obscured
|
||||
for(var/eye in seenby)
|
||||
var/mob/aiEye/m = eye
|
||||
if(!m || !m.ai)
|
||||
var/mob/camera/aiEye/m = eye
|
||||
if(!m)
|
||||
continue
|
||||
if(m.ai.client)
|
||||
m.ai.client.images -= t.obscured
|
||||
var/client/client = m.GetViewerClient()
|
||||
if(client)
|
||||
client.images -= t.obscured
|
||||
|
||||
for(var/turf in visRemoved)
|
||||
var/turf/t = turf
|
||||
@@ -116,32 +118,32 @@
|
||||
|
||||
obscured += t.obscured
|
||||
for(var/eye in seenby)
|
||||
var/mob/aiEye/m = eye
|
||||
if(!m || !m.ai)
|
||||
var/mob/camera/aiEye/m = eye
|
||||
if(!m)
|
||||
seenby -= m
|
||||
continue
|
||||
if(m.ai.client)
|
||||
m.ai.client.images += t.obscured
|
||||
var/client/client = m.GetViewerClient()
|
||||
if(client)
|
||||
client.images += t.obscured
|
||||
|
||||
// Create a new camera chunk, since the chunks are made as they are needed.
|
||||
|
||||
/datum/camerachunk/New(loc, x, y, z)
|
||||
|
||||
// 0xf = 15
|
||||
x &= ~0xf
|
||||
y &= ~0xf
|
||||
x &= ~(CHUNK_SIZE - 1)
|
||||
y &= ~(CHUNK_SIZE - 1)
|
||||
|
||||
src.x = x
|
||||
src.y = y
|
||||
src.z = z
|
||||
|
||||
for(var/obj/machinery/camera/c in range(16, locate(x + 8, y + 8, z)))
|
||||
for(var/obj/machinery/camera/c in urange(CHUNK_SIZE, locate(x + (CHUNK_SIZE / 2), y + (CHUNK_SIZE / 2), z)))
|
||||
if(c.can_use())
|
||||
cameras += c
|
||||
|
||||
for(var/turf/t in range(10, locate(x + 8, y + 8, z)))
|
||||
if(t.x >= x && t.y >= y && t.x < x + 16 && t.y < y + 16)
|
||||
turfs[t] = t
|
||||
for(var/turf/t in block(locate(x, y, z), locate(min(x + CHUNK_SIZE - 1, world.maxx), min(y + CHUNK_SIZE - 1, world.maxy), z)))
|
||||
turfs[t] = t
|
||||
|
||||
for(var/camera in cameras)
|
||||
var/obj/machinery/camera/c = camera
|
||||
@@ -152,6 +154,9 @@
|
||||
continue
|
||||
|
||||
for(var/turf/t in c.can_see())
|
||||
// Possible optimization: if(turfs[t]) here, rather than &= turfs afterwards.
|
||||
// List associations use a tree or hashmap of some sort (alongside the list itself)
|
||||
// so are surprisingly fast. (significantly faster than var/thingy/x in list, in testing)
|
||||
visibleTurfs[t] = t
|
||||
|
||||
// Removes turf that isn't in turfs.
|
||||
|
||||
@@ -3,57 +3,27 @@
|
||||
// An invisible (no icon) mob that the AI controls to look around the station with.
|
||||
// It streams chunks as it moves around, which will show it what the AI can and cannot see.
|
||||
|
||||
/mob/aiEye
|
||||
/mob/camera/aiEye
|
||||
name = "Inactive AI Eye"
|
||||
icon = 'icons/mob/AI.dmi'
|
||||
|
||||
icon = 'icons/mob/AI.dmi' //Allows ghosts to see what the AI is looking at.
|
||||
icon_state = "eye"
|
||||
alpha = 127
|
||||
invisibility = SEE_INVISIBLE_OBSERVER
|
||||
|
||||
var/list/visibleCameraChunks = list()
|
||||
var/mob/living/silicon/ai/ai = null
|
||||
density = 0
|
||||
status_flags = GODMODE // You can't damage it.
|
||||
mouse_opacity = 0
|
||||
see_in_dark = 7
|
||||
invisibility = SEE_INVISIBLE_OBSERVER
|
||||
var/ghostimage = null
|
||||
|
||||
/mob/aiEye/New()
|
||||
ghostimage = image(src.icon,src,src.icon_state)
|
||||
ghost_darkness_images |= ghostimage //so ghosts can see the blob cursor when they disable darkness
|
||||
updateallghostimages()
|
||||
..()
|
||||
|
||||
/mob/aiEye/Destroy()
|
||||
if (ghostimage)
|
||||
ghost_darkness_images -= ghostimage
|
||||
qdel(ghostimage)
|
||||
ghostimage = null;
|
||||
updateallghostimages()
|
||||
ai = null
|
||||
return ..()
|
||||
|
||||
// Movement code. Returns 0 to stop air movement from moving it.
|
||||
/mob/aiEye/Move()
|
||||
return 0
|
||||
|
||||
// Hide popout menu verbs
|
||||
/mob/aiEye/examinate(atom/A as mob|obj|turf in view())
|
||||
set popup_menu = 0
|
||||
set src = usr.contents
|
||||
return 0
|
||||
var/relay_speech = FALSE
|
||||
|
||||
|
||||
// Use this when setting the aiEye's location.
|
||||
// It will also stream the chunk that the new loc is in.
|
||||
/mob/aiEye/setLoc(var/T, var/cancel_tracking = 1)
|
||||
|
||||
/mob/camera/aiEye/setLoc(T)
|
||||
|
||||
if(ai)
|
||||
if(!isturf(ai.loc))
|
||||
return
|
||||
|
||||
if(cancel_tracking)
|
||||
ai.ai_cancel_tracking()
|
||||
|
||||
T = get_turf(T)
|
||||
loc = T
|
||||
cameranet.visibility(src)
|
||||
@@ -63,40 +33,30 @@
|
||||
if(ai.holo)
|
||||
ai.holo.move_hologram()
|
||||
|
||||
/mob/aiEye/proc/getLoc()
|
||||
|
||||
if(ai)
|
||||
if(!isturf(ai.loc) || !ai.client)
|
||||
return
|
||||
return ai.eyeobj.loc
|
||||
|
||||
/mob/aiEye/experience_pressure_difference()
|
||||
/mob/camera/aiEye/Move()
|
||||
return 0
|
||||
|
||||
// AI MOVEMENT
|
||||
/mob/camera/aiEye/proc/GetViewerClient()
|
||||
if(ai)
|
||||
return ai.client
|
||||
return null
|
||||
|
||||
// The AI's "eye". Described on the top of the page.
|
||||
|
||||
/mob/living/silicon/ai
|
||||
var/mob/aiEye/eyeobj = new()
|
||||
var/sprint = 10
|
||||
var/cooldown = 0
|
||||
var/acceleration = 1
|
||||
var/obj/machinery/hologram/holopad/holo = null
|
||||
|
||||
// Intiliaze the eye by assigning it's "ai" variable to us. Then set it's loc to us.
|
||||
/mob/living/silicon/ai/New()
|
||||
..()
|
||||
eyeobj.ai = src
|
||||
eyeobj.name = "[src.name] (AI Eye)" // Give it a name
|
||||
spawn(5)
|
||||
eyeobj.loc = src.loc
|
||||
/mob/camera/aiEye/Destroy()
|
||||
ai = null
|
||||
return ..()
|
||||
|
||||
/atom/proc/move_camera_by_click()
|
||||
if(istype(usr, /mob/living/silicon/ai))
|
||||
var/mob/living/silicon/ai/AI = usr
|
||||
if(AI.eyeobj && AI.client.eye == AI.eyeobj)
|
||||
AI.eyeobj.setLoc(src)
|
||||
AI.cameraFollow = null
|
||||
if (isturf(src.loc) || isturf(src))
|
||||
AI.eyeobj.setLoc(src)
|
||||
|
||||
/mob/aiEye/experience_pressure_difference()
|
||||
return 0
|
||||
|
||||
// AI MOVEMENT
|
||||
|
||||
// This will move the AIEye. It will also cause lights near the eye to light up, if toggled.
|
||||
// This is handled in the proc below this one.
|
||||
@@ -120,38 +80,42 @@
|
||||
else
|
||||
user.sprint = initial
|
||||
|
||||
if(!user.tracking)
|
||||
user.cameraFollow = null
|
||||
|
||||
//user.unset_machine() //Uncomment this if it causes problems.
|
||||
//user.lightNearbyCamera()
|
||||
|
||||
if (user.camera_light_on)
|
||||
user.light_cameras()
|
||||
|
||||
// Return to the Core.
|
||||
|
||||
/mob/living/silicon/ai/proc/core()
|
||||
set category = "AI Commands"
|
||||
set name = "AI Core"
|
||||
|
||||
view_core()
|
||||
|
||||
|
||||
/mob/living/silicon/ai/proc/view_core()
|
||||
|
||||
current = null
|
||||
cameraFollow = null
|
||||
unset_machine()
|
||||
|
||||
if(!src.eyeobj)
|
||||
if(src.eyeobj && src.loc)
|
||||
src.eyeobj.loc = src.loc
|
||||
else
|
||||
src << "ERROR: Eyeobj not found. Creating new eye..."
|
||||
src.eyeobj = new(loc)
|
||||
src.eyeobj = new(src.loc)
|
||||
src.eyeobj.ai = src
|
||||
src.rename_character(null, real_name)
|
||||
src.eyeobj.name = "[src.name] (AI Eye)" // Give it a name
|
||||
|
||||
if(client && client.eye)
|
||||
client.eye = src
|
||||
for(var/datum/camerachunk/c in eyeobj.visibleCameraChunks)
|
||||
c.remove(eyeobj)
|
||||
src.eyeobj.setLoc(src)
|
||||
eyeobj.setLoc(loc)
|
||||
|
||||
/mob/living/silicon/ai/proc/toggle_acceleration()
|
||||
set category = "AI Commands"
|
||||
set name = "Toggle Camera Acceleration"
|
||||
|
||||
if(usr.stat == 2)
|
||||
return //won't work if dead
|
||||
acceleration = !acceleration
|
||||
usr << "Camera acceleration has been toggled [acceleration ? "on" : "off"]."
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
set desc = "Delete image"
|
||||
set src in usr
|
||||
|
||||
deletepicture()
|
||||
deletepicture(src)
|
||||
|
||||
/obj/item/device/camera/siliconcam/robot_camera/verb/take_image()
|
||||
set category ="Robot Commands"
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
################################
|
||||
# Example Changelog File
|
||||
#
|
||||
# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
|
||||
#
|
||||
# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
|
||||
# When it is, any changes listed below will disappear.
|
||||
#
|
||||
# Valid Prefixes:
|
||||
# bugfix
|
||||
# wip (For works in progress)
|
||||
# tweak
|
||||
# soundadd
|
||||
# sounddel
|
||||
# rscadd (general adding of nice things)
|
||||
# rscdel (general deleting of nice things)
|
||||
# imageadd
|
||||
# imagedel
|
||||
# spellcheck (typo fixes)
|
||||
# experiment
|
||||
#################################
|
||||
|
||||
# Your name. Remove the quotation mark and put in your name when copy+pasting the example changelog.
|
||||
author: Tigercat2000
|
||||
|
||||
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
|
||||
delete-after: True
|
||||
|
||||
# Any changes you've made. See valid prefix list above.
|
||||
# INDENT WITH TWO SPACES. NOT TABS. SPACES.
|
||||
# SCREW THIS UP AND IT WON'T WORK.
|
||||
# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit.
|
||||
# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog.
|
||||
changes:
|
||||
- bugfix: "Fixed a bug where the AI could not delete photos it takes."
|
||||
- rscadd: "Ported -tg- cameranets. This means that all of our camera based systems are more or less up to date with -tg-; 510 features are missing."
|
||||
- rscadd: "An AI with the 'camera lights' mode on will now, instead of the button toggling the closest camera's light, toggle camera lights on/off as the AI moves."
|
||||
- rscadd: "It now takes a minimum of 30 deciseconds between attempting to track someone, and locking on. This number is increased by their distance away from the AI eye."
|
||||
- tweak: "The camera activate/deactivate proc is now called 'toggle_cam'."
|
||||
- tweak: "The 'trackable' proc has been replaced by a more object-oriented 'mob.can_track()' proc."
|
||||
- tweak: "The 'networks' section of the security camera console is now above the camera list."
|
||||
Vendored
+5
-5
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -5,33 +5,38 @@ Used In File(s): \code\game\machinery\computer\camera.dm
|
||||
{{:helper.link('Show Map', 'thumb-tack', {'showMap' : 1})}}
|
||||
{{:helper.link('Reset', 'refresh', {'reset' : 1})}}
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Current Camera: </div>
|
||||
{{if data.current}}
|
||||
<div class='itemContent'><b>{{:data.current.name}}</b></div>
|
||||
{{else}}
|
||||
<div class='itemContent'>None</div>
|
||||
{{/if}}
|
||||
<div class='itemLabel'>Current Camera: </div>
|
||||
{{if data.current}}
|
||||
<div class='itemContent'><b>{{:data.current.name}}</b></div>
|
||||
{{else}}
|
||||
<div class='itemContent'>None</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{for data.cameras}}
|
||||
{{if data.current && value.name == data.current.name}}
|
||||
{{:helper.link(value.name, '', {'switchTo' : value.camera}, 'selected')}}
|
||||
{{else value.deact}}
|
||||
{{:helper.link(value.name + " (deactivated)", '', {}, 'inactive')}}
|
||||
{{else}}
|
||||
{{:helper.link(value.name, '', {'switchTo' : value.camera})}}
|
||||
{{/if}}
|
||||
{{/for}}
|
||||
|
||||
{{if data.networks}}
|
||||
<div class="item">
|
||||
<h2>Networks</h2>
|
||||
Please select the networks you'd like this console to monitor.
|
||||
{{if data.emagged}}
|
||||
<div class="bad">WARNING: Unauthorized access detected.</div>
|
||||
{{/if}}
|
||||
<div class="item">
|
||||
{{for data.networks}}
|
||||
{{:helper.link(value.name, value.active ? 'minus' : 'plus', { 'activate' : value.name, 'active' : value.active }, '', value.active ? 'linkOn' : '')}}
|
||||
{{/for}}
|
||||
</div>
|
||||
<h2>Networks</h2>
|
||||
Please select the networks you'd like this console to monitor.
|
||||
{{if data.emagged}}
|
||||
<div class="bad">WARNING: Unauthorized access detected.</div>
|
||||
{{/if}}
|
||||
<div class="item">
|
||||
{{for data.networks}}
|
||||
{{:helper.link(value.name, value.active ? 'minus' : 'plus', { 'activate' : value.name, 'active' : value.active }, '', value.active ? 'linkOn' : '')}}
|
||||
{{/for}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="item">
|
||||
<h2>Cameras</h2>
|
||||
{{for data.cameras}}
|
||||
{{if data.current && value.name == data.current.name}}
|
||||
{{:helper.link(value.name, '', {'switchTo' : value.camera}, 'selected')}}
|
||||
{{else value.deact}}
|
||||
{{:helper.link(value.name + " (deactivated)", '', {}, 'inactive')}}
|
||||
{{else}}
|
||||
{{:helper.link(value.name, '', {'switchTo' : value.camera})}}
|
||||
{{/if}}
|
||||
{{/for}}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user