Merge pull request #3865 from tigercat2000/tgcam

Port -tg-station cameranets
This commit is contained in:
Fox McCloud
2016-03-15 11:04:25 -04:00
26 changed files with 520 additions and 458 deletions
+6 -1
View File
@@ -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
+19
View File
@@ -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)
+3 -4
View File
@@ -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"
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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>"
+80 -70
View File
@@ -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,24 @@
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)
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 +85,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 +111,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 +196,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 +219,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 +229,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 +237,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 +275,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 +328,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 +336,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 +352,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)
+95 -94
View File
@@ -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
+64 -74
View File
@@ -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
+1 -1
View File
@@ -46,7 +46,7 @@
var/found_eye = 0
for(var/mob/aiEye/A in living_mob_list)
for(var/mob/camera/aiEye/A in living_mob_list)
var/turf/our_turf = get_turf(src)
var/turf/eye_turf = get_turf(A)
-1
View File
@@ -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)
+2 -2
View File
@@ -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()
+15 -3
View File
@@ -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("")
@@ -1913,5 +1913,17 @@
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 ..()
/mob/living/carbon/human/proc/get_age_pitch()
return 1.0 + 0.5*(30 - age)/80
+25
View File
@@ -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
+61 -38
View File
@@ -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,27 @@
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)
// 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 +77,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"]."
+2 -2
View File
@@ -108,7 +108,7 @@ proc/isembryo(A)
return 1
/proc/isAIEye(A)
if(istype(A, /mob/aiEye))
if(istype(A, /mob/camera/aiEye))
return 1
return 0
@@ -379,7 +379,7 @@ proc/Gibberish(t, p)//t is the inputted message, and any value higher than 70 fo
var/atom/oldeye=M.client.eye
var/aiEyeFlag = 0
if(istype(oldeye, /mob/aiEye))
if(istype(oldeye, /mob/camera/aiEye))
aiEyeFlag = 1
var/x
@@ -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"